package Traductor.sintactico;

import java.util.ArrayList;
import java.util.HashMap;

import Traductor.lexico.utils.Token;
import Traductor.lexico.utils.Token.TipoToken;
import Traductor.sintactico.utils.*;
import Traductor.sintactico.utils.ExprTipo.Tipo;
import Traductor.sintactico.utils.TablaOperaciones.Operadores;

/**
 * Clase que integra toda la funcionalidad necesaria para analizar sintácticamente una lista de tokens producida por el analizador léxico.
 */
public class AnalizadorSintactico {

	private ArrayList<Token>			listaTokens;
	private Token						tokenActual;
	private ArrayList<String>			codString;
	private TablaOperaciones			tablas;
	private int							numTkn;
	private ArrayList<ErrorContextual>	erroresC;
	private int							etq;
	private final int					longprologo		= 13;
	private final int					longepilogo		= 13;
	private final int					longinicio		= 4;
	private final int					longApilaRet	= 5;
	private final int					longFinPaso		= 1;
	private final String				finPaso			= "desapila";
	private final int					longInicioPaso	= 3;

	public AnalizadorSintactico(ArrayList<Token> tokens) {
		this.listaTokens = tokens;
		this.tablas = new TablaOperaciones();
		this.numTkn = 0;
		this.etq = 0;
		this.codString = new ArrayList<String>();
		erroresC = new ArrayList<ErrorContextual>();
	}

	/**
	 * Itera sobre la lista de tokens pasando al siguiente.
	 */
	private void getNextToken() {
		tokenActual = listaTokens.get(numTkn);
		numTkn++;
	}

	/**
	 * Analiza sintacticamente el programa entero, emitiendo el "stop" final si no ha habido errores sintácticos.
	 * 
	 * @return El par con el código maquina emitido y un booleano que simboliza si ha habido errores
	 * @throws Exception
	 *             En caso de fallo sintáctico
	 */
	public DatosProg Prog() throws Exception {
		// Prog -> Cabecera: iden. Declaraciones: Decs Instrucciones: Is EOF
		int numNiveles = 0;
		int tamDatos = 0;
		boolean decs = false;
		emite("seg(?)");
		etq = etq + 1;
		inicio(0, 0);
		emite("ir-a(?)");
		etq = etq + longinicio + 1;
		getNextToken();
		if (!coincide(TipoToken.cabecera))
			throw new Exception("Palabra reservada \"programa\" esperada en la posicion: " + getToken().getPosicion());
		getNextToken();
		if (!coincide(TipoToken.dospuntos))
			throw new Exception("Falta dos puntos en la posicion: " + getToken().getPosicion());
		getNextToken();
		if (!coincide(TipoToken.iden))
			throw new Exception("Identificador \"" + getToken().getLexema() + "\" incorrecto en la posicion: " + getToken().getPosicion());
		getNextToken();
		if (!coincide(TipoToken.punto))
			throw new Exception("Falta punto en la posicion: " + getToken().getPosicion());
		getNextToken();
		TablaSimbolos ts = creaTS();
		DatosDecs par1 = new DatosDecs(ts, false, 0, 0, null);
		if (coincide(TipoToken.declaraciones)) {
			getNextToken();
			if (!coincide(TipoToken.dospuntos))
				throw new Exception("Falta dos puntos en la posicion: " + getToken().getPosicion());
			par1 = Decs(ts, 0, 0);
			numNiveles = par1.getNivel();
			tamDatos = par1.getDireccion();
			decs = true;
		}
		else
			if (coincide(TipoToken.instrucciones))
				par1.setError(false);
			else {
				throw new Exception("Palabra reservada 'declaraciones' o 'instrucciones' esperada en la posicion: " + getToken().getPosicion());
			}
		if (!coincide(TipoToken.instrucciones))
			throw new Exception("Se esperaba palabra reservada 'instrucciones' en la posicion: " + getToken().getPosicion());
		getNextToken();
		if (!coincide(TipoToken.dospuntos))
			throw new Exception("Falta dos puntos en la posicion: " + getToken().getPosicion());
		if (decs) {
			parchea(1, numNiveles + 2);
			parchea(3, 1 + numNiveles + tamDatos);
			parchea(5, etq);
			parchea(0, tamDatos + numNiveles + 2);
		}
		else {
			parchea(0, 2);
			parchea(1, 0);
			parchea(3, 0);
			parchea(5, etq);
		}
		boolean err2 = Is(ts, 0);// analizamos las instrucciones
		if (coincide(TipoToken.EOF)) {// comprobamos que hemos llegado a fin de
			emite("stop");
			etq = etq + 1;
		}
		else
			throw new Exception("El archivo no finalizo correctamente.");
		int nPend = 0;
		if (par1.getPendientes() != null)
			nPend = par1.getPendientes().getPend().size();
		if (nPend > 0){
			erroresC.add(new ErrorContextual("Hay tipos sin definir.\n"));
		}
		return new DatosProg(codString2cod(codString), err2 || par1.isError() || nPend > 0);
	}

	
	/** Convierte un ArrayList<String> de entrada a una unica cadena String de salida que contiene la totalidad del array
	 * @param codigo El arrayList<String> que contiene todo el codigo emitido en el programa
	 * @return Una unica cadena String que contiene todo el codigo emitido 
	 */
	private String codString2cod(ArrayList<String> codigo) {
		String resp = "";
		for (int i = 0; i < codigo.size(); i++) {
			resp += codigo.get(i) + "\n";
		}
		return resp;
	}

	// *****************************DECLARACIONES************************
	/**
	 * Analiza sintacticamente todas las declaraciones del programa. La primera es obligatoria (primera llamada a "Dec()"), el resto son opcionales.
	 * 
	 * @return Devuelve un booleano de error
	 * @throws Exception
	 *             Propaga las excepciones de los metodos a los que llama
	 */
	private DatosDecs Decs(TablaSimbolos tsph, int nh, int dirh) throws Exception {
		// Decs ->Dec RDecs
		Declaracion dec = Dec(tsph, nh, dirh);
		Pendientes pends = dec.getPendientes();
		boolean errS = false;
		if (!dec.isError()) {
			if (tsph.existeId(dec.getId(), dec.getNivel()) && tsph.nivelDe(dec.getId(), dec.getNivel()) == nh) {
				erroresC.add(new ErrorContextual("La variable '" + dec.getId() + "' ya ha sido declarada en esta seccion\n"));
				errS = true;
			}
			else {
				tsph.anadeId(dec.getId(), dec.getClase(), dec.getDireccion(), dec.getExpresion(), dec.getNivel(), dec.getInicio());
				if (dec.getClase() == TipoToken.variable) {
					dirh = dirh + dec.getExpresion().getTamaño();
				}
				if (dec.getClase() == TipoToken.tipo) {
					if (pends.getPend().contains(dec.getId()))
						pends.eliminaId(dec.getId(), dec.getNivel());
				}
			}
		}
		return RDecs(dec.isError() || errS, tsph, dec.getNivel(), dirh, nh, pends);
	}

	/**
	 * Analiza sintacticamente las declaraciones a partir de la primera, si las hubiera
	 * 
	 * @param dirh
	 *            La dirección de memoria a partir de la cual introducir una variable en la tabla de simbolos
	 * @return Una estructura de tipo DatosDecs que contiene entre otras cosas los posibles errores que hayan surgido
	 * @throws Exception
	 *             En caso de error sintáctico
	 */
	private DatosDecs RDecs(boolean errh, TablaSimbolos tsph, int nh, int dirh, int nivelActual, Pendientes pendh) throws Exception {
		// RDecs->; Dec RDecs
		boolean errS = errh;
		Pendientes pends = pendh;
		getNextToken();
		int ns = nh;
		while (coincide(TipoToken.puntoycoma)) {
			Declaracion dec = Dec(tsph, nivelActual, dirh);
			if (!dec.isError()) {
				if (tsph.existeId(dec.getId(), dec.getNivel()) && tsph.nivelDe(dec.getId(), dec.getNivel()) == nivelActual) {
					erroresC.add(new ErrorContextual("La variable '" + dec.getId() + "' ya ha sido declarada en esta seccion\n"));
					errS = true;
				}
				else {
					tsph.anadeId(dec.getId(), dec.getClase(), dec.getDireccion(), dec.getExpresion(), nivelActual, dec.getInicio());
					ns = Math.max(dec.getNivel(), ns);
					if (dec.getClase() == TipoToken.variable) {
						dirh = dirh + dec.getExpresion().getTamaño();
					}
					if (dec.getClase() == TipoToken.tipo) {
						if (pends.existeID(dec.getId(), dec.getNivel()))
							pends.eliminaId(dec.getId(), dec.getNivel());
					}
				}
			}
			else {
				errS = true;
			}
			getNextToken();
		}
		if (coincide(TipoToken.iden) || coincide(TipoToken.variable))
			throw new Exception("Falta punto y coma en la posicion: " + getToken().getPosicion());
		else
			if (coincide(TipoToken.instrucciones)) {
				return new DatosDecs(tsph, errS, ns, dirh, pendh);
			}
			else
				throw new Exception("Falta declaracion en la posicion: " + getToken().getPosicion());
	}

	/**
	 * Analiza sintacticamente la primera declaracion de variables del programa.
	 * @param tsph La tabla de simbolos actual
	 * @param nh El nivel
	 * @return Un objeto de clase Declaracion, que modela una declaracion de variables 
	 * @throws Exception En caso de error sintactico
	 */
	private Declaracion Dec(TablaSimbolos tsph, int nh, int dirh) throws Exception {
		getNextToken();
		if (coincide(TipoToken.variable)) {
			return DecVar(tsph, nh, dirh);
		}
		else
			if (coincide(TipoToken.tipo)) {
				return DecTipo(tsph, nh);
			}
			else
				if (coincide(TipoToken.procedimiento)) {
					return DecProc(tsph, nh);
				}
				else
					throw new Exception("Se esperaba palabra reservada 'variable', 'tipo' o 'procedimiento'. Posicion: " + tokenActual.getPosicion());
	}

	/**
	 * Analiza sintacticamente una unica declaración de variable del programa
	 * 
	 * @return Un objeto de clase Declaracion, que modela una declaracion de variables 
	 * @throws Exception
	 *             En caso de error sintáctico
	 */
	private Declaracion DecVar(TablaSimbolos tsph, int nh, int dirh) throws Exception {
		// DecVar -> variable identificador tipo tipovariable
		if (!coincide(TipoToken.variable))
			throw new Exception("Palabra reservada \"variable\" esperada en la posicion: " + getToken().getPosicion());
		getNextToken();
		if (!coincide(TipoToken.iden))
			throw new Exception("Identificador incorrecto en la posicion: " + getToken().getPosicion());
		Token tAux = tokenActual;
		getNextToken();
		if (!coincide(TipoToken.tipo))
			throw new Exception("Palabra reservada \"tipo\" esperada en la posicion: " + getToken().getPosicion());
		getNextToken();
		DatosExpr aux = DTipo(tsph, nh, false);
		boolean error = aux.isError();
		if (error) {
			Declaracion resp = new Declaracion(TipoToken.ERROR, true);
			return resp;
		}
		else {
			if (tsph.existeId(tAux.getLexema(), nh)) {
				erroresC.add(new ErrorContextual("La variable " + tAux.getLexema() + " ya ha sido declarada. Linea: " + tAux.getLinea() + "\n"));
				error = true;
				Declaracion resp = new Declaracion(TipoToken.ERROR, true);
				return resp;
			}
			if (referenciaErronea(aux.getE(), tsph, nh)) {
				erroresC.add(new ErrorContextual("El tipo '" + (aux.getE()).getId() + "' no esta declarado en el momento de la declaracion de la variable '" + tAux.getLexema() + "':"
						+ tAux.getPosicion() + "\n"));
				Declaracion resp = new Declaracion(TipoToken.ERROR, true);
				error = true;
				return resp;
			}
			Declaracion resp = new Declaracion(tAux.getLexema(), TipoToken.variable, aux.getE(), nh, dirh, aux.getPendientes());
			resp.setError(false);
			return resp;
		}
	}

	/**
	 * Analiza sintacticamente una unica declaración de tipo del programa
	 * 
	 * @return Un objeto de clase Declaracion, que modela una declaracion de variables 
	 * @throws Exception
	 *             En caso de error sintáctico
	 */
	private Declaracion DecTipo(TablaSimbolos tsph, int nh) throws Exception {
		// DecTipo -> tipo identificador = DTipo
		getNextToken();
		if (!coincide(TipoToken.iden)) {
			throw new Exception("Se esperaba un identificador en la posicion " + getToken().getPosicion());
		}
		Token tAux = tokenActual;
		getNextToken();
		if (!coincide(TipoToken.asig)) {
			throw new Exception("Se esperaba '=' en la posicion " + getToken().getPosicion());
		}
		getNextToken();
		DatosExpr aux = DTipo(tsph, nh, false);
		boolean error = aux.isError();
		if (tsph.existeIdAnt(tAux.getLexema(), nh) != -1) {
			erroresC.add(new ErrorContextual("El tipo " + tAux.getLexema() + " ya ha sido declarado. Linea: " + tAux.getLinea() + "\n"));
			error = true;
		}
		if (referenciaErronea(aux.getE(), tsph, nh)) {
			erroresC.add(new ErrorContextual("El tipo " + aux.getE().getId() + " no esta declarado.\n"));
			error = true;
		}
		if (error) {
			Declaracion resp = new Declaracion(TipoToken.ERROR, true);
			return resp;
		}
		else {
			Declaracion resp = new Declaracion(tAux.getLexema(), TipoToken.tipo, aux.getE(), nh, -1, aux.getPendientes());
			resp.setError(false);
			return resp;
		}
	}

	/**
	 * Analiza sintacticamente el tipo declarado en la declaracion de tipo previa
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Una clase de tipo DatosExpr que contiene informacion acerca del tipo de datos modelado (vector, registro, identificador, etc.)
	 * @throws Exception En caso de error sintáctico
	 */
	private DatosExpr DTipo(TablaSimbolos tsph, int nh, boolean punteroVa) throws Exception {
		boolean error;
		Pendientes pends = new Pendientes();
		if (coincideTipo()) {
			return TBasico();
		}
		else
			if (coincide(TipoToken.vector))
				// TArray
				return TArray(tsph, nh);
			else
				if (coincide(TipoToken.registro))
					return TReg(tsph, nh);
				else
					if (coincide(TipoToken.puntero))
						// TPuntero
						return TPuntero(tsph, nh);
					else
						if (coincide(TipoToken.iden)) {
							ExprTipo e;
							int nTipo = tsph.existeIdAnt(tokenActual.getLexema(), nh);
							if (nTipo != -1) {
								if (tsph.claseDe(tokenActual.getLexema(), nTipo) != TipoToken.tipo) {
									erroresC.add(new ErrorContextual("El identificador " + tokenActual.getLexema() + " debe ser un tipo.\n"));
									error = true;
								}
								else
									error = false;
								e = new ExprTipo(Tipo.ref, tsph.exprDe(tokenActual.getLexema(), nTipo).getTamaño(), tokenActual.getLexema());
							}
							else {
								// if(punteroVa){
								// solo se añaden a pendientes los punteros
								pends.anyadeId(tokenActual.getLexema(), nh);
								e = new ExprTipo(Tipo.ref, 0, tokenActual.getLexema());
								error = false;
								// }
								// else{
								// e = new ExprTipo(Tipo.err, -1, tokenActual.getLexema());
								// erroresC.add(new ErrorContextual("El tipo de esta declaracion no ha sido declarado correctamente.\n"));
								// error = true;
								// }
							}
							return new DatosExpr(e, error, pends);
						}
						else
							throw new Exception("Declaracion incorrecta de tipo en la posicion: " + getToken().getPosicion());
	}

	/**
	 * Analiza sintacticamente la declaracion del puntero
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Una clase de tipo DatosExpr que contiene informacion acerca del tipo de datos modelado (vector, registro, identificador, etc.)
	 * @throws Exception En caso de error sintáctico
	 */
	private DatosExpr TPuntero(TablaSimbolos tsph, int nh) throws Exception {
		getNextToken();
		DatosExpr aux = DTipo(tsph, nh, true);
		if (aux.isError()) {
			return new DatosExpr(null, true, aux.getPendientes());
		}
		ExprTipo resp = new ExprTipo(Tipo.punt, 1, aux.getE());
		return new DatosExpr(resp, false, aux.getPendientes());
	}

	/**
	 * Analiza sintacticamente la declaracion del array
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Una clase de tipo DatosExpr que contiene informacion acerca del tipo de datos modelado (vector, registro, identificador, etc.)
	 * @throws Exception En caso de error sintáctico
	 */
	private DatosExpr TArray(TablaSimbolos tsph, int nh) throws Exception {
		getNextToken();
		if (!coincide(TipoToken.corA))
			throw new Exception("Se esperaba '[' en la posicion: " + getToken().getPosicion());
		getNextToken();
		if (!coincide(TipoToken.nat))
			throw new Exception("Se esperaba un valor entero positivo en la posicion: " + getToken().getPosicion());
		int numE = Integer.valueOf(tokenActual.getLexema());
		getNextToken();
		if (!coincide(TipoToken.corC))
			throw new Exception("Se esperaba ']' en la posicion: " + getToken().getPosicion());
		getNextToken();
		if (!coincide(TipoToken.de))
			throw new Exception("Se esperaba 'de' en la posicion: " + getToken().getPosicion());
		getNextToken();
		DatosExpr e = DTipo(tsph, nh, false);
		boolean error = false;
		if (e.isError()) {
			error = true;
			return new DatosExpr(null, error, null);
		}
		if (referenciaErronea(e.getE(), tsph, nh)) {
			error = true;
			erroresC.add(new ErrorContextual("El tipo del vector no existe o no ha sido definido correctamente. Posicion: " + tokenActual.getPosicion() + "\n"));
			return new DatosExpr(null, error, null);
		}
		ExprTipo resp = new ExprTipo(Tipo.array, numE * e.getE().getTamaño(), numE, e.getE());
		return new DatosExpr(resp, error, e.getPendientes());
	}

	/**
	 * Analiza sintacticamente la declaracion del registro
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Una clase de tipo DatosExpr que contiene informacion acerca del tipo de datos modelado (vector, registro, identificador, etc.)
	 * @throws Exception En caso de error sintáctico
	 */
	private DatosExpr TReg(TablaSimbolos tsph, int nh) throws Exception {
		getNextToken();
		if (!coincide(TipoToken.llaveA))
			throw new Exception("Se esperaba '{' en la posicion: " + getToken().getPosicion());
		ListaDeCampos listaCampos = Campos(tsph, nh);
		if (!coincide(TipoToken.llaveC))
			throw new Exception("Se esperaba '}' en la posicion: " + getToken().getPosicion());
		if (listaCampos.isError()) {
			return new DatosExpr(null, true, null);
		}
		ExprTipo aux = new ExprTipo(ExprTipo.Tipo.rec, listaCampos.getTam(), listaCampos);
		return new DatosExpr(aux, false, listaCampos.getPendientes());
	}

	/**
	 * Analiza sintacticamente los campos dentro del registro
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Una clase de tipo ListaDeCampos que contiene informacion acerca de los campos del registro
	 * @throws Exception En caso de error sintáctico
	 */
	private ListaDeCampos Campos(TablaSimbolos tsph, int nh) throws Exception {
		ExprCampo cam = Campo(0, tsph, nh);
		Pendientes pends = cam.getPendientes();
		ListaDeCampos campos = new ListaDeCampos();
		if (cam.isError()) {
			erroresC.add(new ErrorContextual("Hubo un error en la generacion del campo '" + cam.getId() + "'. Linea: " + tokenActual.getLinea() + "\n"));
			campos.setError(true);
		}
		else {
			campos.anyadeCampo(cam);
			campos.setTam(cam.getTam());
			campos.setPendientes(cam.getPendientes());
		}
		return RCampos(campos, tsph, nh, pends);
	}

	/**
	 * Analiza sintacticamente el primer campo dentro del registro
	 * @param desph El desplazamiento
	 * @param tsph La tabla de simbolos
	 * @param nh EL nivel
	 * @return Una clase de tipo ExprCampo que contiene informacion acerca de campo del registro
	 * @throws Exception EN caso de error sintáctico
	 */
	private ExprCampo Campo(int desph, TablaSimbolos tsph, int nh) throws Exception {
		getNextToken();
		if (!coincide(TipoToken.iden))
			throw new Exception("Se esperaba un identificador en la posicion: " + getToken().getPosicion());
		String id = tokenActual.getLexema();
		getNextToken();
		if (!coincide(TipoToken.tipo)) {
			throw new Exception("Se esperaba 'tipo' en la posicion: " + getToken().getPosicion());
		}
		getNextToken();
		DatosExpr aux = DTipo(tsph, nh, false);
		boolean error = false;
		if (aux.isError())
			error = true;
		if (referenciaErronea(aux.getE(), tsph, nh)) {
			erroresC.add(new ErrorContextual("El tipo '" + aux.getE().getId() + "' hace referencia a un tipo que no ha sido declarado aun.\n"));
			error = true;
		}
		return new ExprCampo(id, aux.getE(), aux.getE().getTamaño(), desph, error, aux.getPendientes());
	}

	
	/**
	 * Analiza sintacticamente los campos del registro a partir del primero
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Una clase de tipo ListaDeCampos que contiene informacion acerca de los campos del registro
	 * @throws Exception En caso de error sintáctico
	 */
	private ListaDeCampos RCampos(ListaDeCampos campos, TablaSimbolos tsph, int nh, Pendientes pendh) throws Exception {
		Pendientes pends = pendh;
		getNextToken();
		while (coincide(TipoToken.puntoycoma)) {
			ExprCampo cam = Campo(campos.getTam(), tsph, nh);
			if (cam.isError()) {
				erroresC.add(new ErrorContextual("Hubo un error en la generacion del campo '" + cam.getId() + "'. Linea: " + tokenActual.getLinea() + "\n"));
				campos.setError(true);
			}
			if (!campos.existeId(cam.getId())) {
				for (int i = 0; i < cam.getPendientes().getPend().size(); i++) {
					pends.getPend().add(cam.getPendientes().getPend().get(i));
				}
				campos.setPendientes(pends);
				campos.anyadeCampo(cam);
				campos.setTam(campos.getTam() + cam.getTam());
			}
			else {
				erroresC.add(new ErrorContextual("El campo " + cam.getId() + " ya ha sido declarado. Linea: " + getToken().getLinea() + "\n"));
				campos.setError(true);
			}
			getNextToken();
		}
		if (coincide(TipoToken.iden))
			throw new Exception("Falta punto y coma en la posicion: " + getToken().getPosicion());
		else
			if (coincide(TipoToken.llaveC)) {
				return campos;
			}
			else
				throw new Exception("Falta declaracion en la posicion: " + getToken().getPosicion());
	}

	/** 
	 * Analiza sintacticamente la declaracion del tipo basico
	 * @return Una clase de tipo DatosExpr que contiene informacion acerca del tipo de datos modelado (vector, registro, identificador, etc.)
	 */
	private DatosExpr TBasico() {
		switch (tokenActual.getTipo()) {
			case tBool:
				return new DatosExpr(new ExprTipo(token2Tipo(tokenActual.getTipo()), 1), false, new Pendientes());
			case tEnt:
				return new DatosExpr(new ExprTipo(token2Tipo(tokenActual.getTipo()), 1), false, new Pendientes());
			case tReal:
				return new DatosExpr(new ExprTipo(token2Tipo(tokenActual.getTipo()), 1), false, new Pendientes());
			case tNat:
				return new DatosExpr(new ExprTipo(token2Tipo(tokenActual.getTipo()), 1), false, new Pendientes());
			case tChar:
				return new DatosExpr(new ExprTipo(token2Tipo(tokenActual.getTipo()), 1), false, new Pendientes());
			case ERROR:
				return new DatosExpr(new ExprTipo(token2Tipo(tokenActual.getTipo()), 1), false, new Pendientes());
		}
		return null;
	}

	/**
	 * Analiza sintacticamente la declaracion de un procedimiento
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Un objeto de clase Declaracion, que modela una declaracion de variables 
	 * @throws Exception En caso de error sintactico
	 */
	private Declaracion DecProc(TablaSimbolos tsph, int nh) throws Exception {
		if (!coincide(TipoToken.procedimiento))
			throw new Exception("Palabra reservada \"procedimiento\" esperada en la posicion: " + getToken().getPosicion());
		getNextToken();
		if (!coincide(TipoToken.iden))
			throw new Exception("Identificador incorrecto en la posicion: " + getToken().getPosicion());
		Token tAux = tokenActual;
		TablaSimbolos tInicial = creaTS(tsph);
		ListaParams lInicial = new ListaParams();
		DatosParams dIniciales = new DatosParams(lInicial, tInicial, nh + 1, tAux);
		DatosParams resultado = FParams(dIniciales);
		ExprTipo aux = new ExprTipo(Tipo.proc, resultado.getLista());
		getNextToken();
		if (!resultado.isError()) {
			DatosBloque bloque = Bloque(resultado);
			if (bloque.isError()) {
				Declaracion d = new Declaracion(TipoToken.ERROR, true);
				return d;
			}
			else {
				return new Declaracion(tAux.getLexema(), TipoToken.procedimiento, aux, bloque.getNivel(), -1, bloque.getInicio());
			}
		}
		else
			return new Declaracion(TipoToken.ERROR, true);
	}

	//TODO Javadoc incompleto aqui ¿Que hace este metodo exactamente?
	private DatosParams FParams(DatosParams datosh) throws Exception {
		getNextToken();
		if (coincide(TipoToken.parA)) {
			DatosParams resp = LFParams(datosh);
			if (!coincide(TipoToken.parC))
				throw new Exception("Se esperaba ')'. Posicion: " + getToken().getPosicion());
			return resp;
		}
		else {
			throw new Exception("Se esperaba '('. Posicion: " + tokenActual.getPosicion());
		}
	}
	//TODO Javadoc incompleto aqui ¿Que hace este metodo exactamente? 
	//Este metodo procesa el interior de lo que hay entre parentesis y parentesis de FParams
	private DatosParams LFParams(DatosParams datosh) throws Exception {
		getNextToken();
		DatosParams datosS = null;
		if (!coincide(TipoToken.parC)) {
			Param p = Fparam(0, datosh.getTs(), datosh.getNivel());
			if (!p.isError()) {
				p.getProps().setDir(0);
				datosh.getTs().anadeId(p.getId(), p.getProps().getNivel(), p.getProps());
				datosh.getLista().anyadeParam(p.getParam());
				datosh.setDireccion(datosh.getDireccion() + p.getTamaño());
			}
			else {
				datosh.setError(true);
			}
			datosS = RParams(datosh);
		}
		else {
			datosS = new DatosParams(new ListaParams(), datosh.getTs(), datosh.getNivel(), tokenActual);
		}
		return datosS;
	}
	//TODO Javadoc incompleto aqui ¿Que hace este metodo exactamente? 
	private DatosParams RParams(DatosParams datosh) throws Exception {
		DatosParams datosS = datosh;
		getNextToken();
		while (coincide(TipoToken.coma)) {
			getNextToken();
			Param p = Fparam(datosS.getDireccion(), datosh.getTs(), datosh.getNivel());
			if (p.isError() || datosS.isError()) {
				datosS.setError(true);
			}
			else
				if (datosS.getTs().existeId(p.getId(), p.getProps().getNivel()) && (datosS.getTs().nivelDe(p.getId(), p.getProps().getNivel()) == datosh.getNivel())) {
					datosS.setError(true);
					erroresC.add(new ErrorContextual("El parametro " + p.getId() + " ya ha sido declarado. Linea: " + getToken().getLinea() + "\n"));
				}
				else {
					datosS.getTs().anadeId(p.getId(), p.getProps().getNivel(), p.getProps());
					p.getProps().setDir(p.getParam().getDir());
					datosS.getLista().anyadeParam(p.getParam());
					datosS.setDireccion(datosS.getDireccion() + p.getTamaño());
				}
			getNextToken();
		}
		return datosS;
	}
	//TODO Javadoc incompleto aqui ¿Que hace este metodo exactamente? 
	private Param Fparam(int dirh, TablaSimbolos tsph, int nh) throws Exception {
		Param p;
		if (coincide(TipoToken.param)) {
			getNextToken();
			if (!coincide(TipoToken.iden)) {
				throw new Exception("Identificador incorrecto en la posicion: " + getToken().getPosicion());
			}
			Token tAux = tokenActual;
			getNextToken();
			if (!coincide(TipoToken.tipo)) {
				throw new Exception("Se esperaba 'tipo' en la posicion: " + getToken().getPosicion());
			}
			getNextToken();
			DatosExpr aux = DTipo(tsph, nh, false);
			p = new Param(tAux.getLexema(), aux.getE().getTamaño());
			p.setProps(new RegistroTS(TipoToken.variable, aux.getE(), nh));
			p.setParam(new ExprParam("valor", aux.getE(), dirh));
			p.setError(aux.isError());
		}
		else
			if (coincide(TipoToken.paramvar)) {
				getNextToken();
				if (!coincide(TipoToken.iden)) {
					throw new Exception("Identificador incorrecto en la posicion: " + getToken().getPosicion());
				}
				Token tAux = tokenActual;
				getNextToken();
				if (!coincide(TipoToken.tipo)) {
					throw new Exception("Se esperaba 'tipo' en la posicion: " + getToken().getPosicion());
				}
				getNextToken();
				DatosExpr aux = DTipo(tsph, nh, false);
				p = new Param(tAux.getLexema(), 1);
				p.setProps(new RegistroTS(TipoToken.paramvar, aux.getE(), nh));
				p.setParam(new ExprParam("variable", aux.getE(), dirh));
				p.setError(aux.isError());
			}
			else {
				throw new Exception("Se esperaba 'param' o 'paramvar' en la posicion: " + getToken().getPosicion());
			}
		return p;
	}

	/**
	 * Analiza sintacticamente el bloque de codigo dentro de la declaracion de un procedimiento
	 * @return Una clase del tipo DatosBloque, con informacion relativa al contenido de este
	 * @throws Exception En caso de error sintactico
	 */
	private DatosBloque Bloque(DatosParams datosh) throws Exception {
		if (!coincide(TipoToken.llaveA)) {
			throw new Exception("Falta '{' en la posicion: " + getToken().getPosicion());
		}
		getNextToken();
		DatosDecs par1 = new DatosDecs(datosh.getTs(), false, 0, datosh.getDireccion(), new Pendientes());
		boolean decs = false;
		if (coincide(TipoToken.declaraciones)) {
			getNextToken();
			if (!coincide(TipoToken.dospuntos))
				throw new Exception("Falta dos puntos en la posicion: " + getToken().getPosicion());
			par1 = Decs(datosh.getTs(), datosh.getNivel(), datosh.getDireccion());
			decs = true;
		}
		else
			if (coincide(TipoToken.instrucciones))
				par1.setError(false);
			else {
				throw new Exception("Palabra reservada 'declaraciones' o 'instrucciones' esperada en la posicion: " + getToken().getPosicion());
			}
		if (!coincide(TipoToken.instrucciones))
			throw new Exception("Se esperaba palabra reservada 'instrucciones' en la posicion: " + getToken().getPosicion());
		getNextToken();
		ExprTipo aux = new ExprTipo(Tipo.proc, datosh.getLista());
		RegistroTS regts = new RegistroTS(TipoToken.procedimiento, -1, aux, datosh.getNivel(), etq);
		datosh.getTs().anadeId(datosh.getT().getLexema(), datosh.getNivel(), regts);
		if (!coincide(TipoToken.dospuntos))
			throw new Exception("Falta dos puntos en la posicion: " + getToken().getPosicion());
		int inicio = etq;
		int tamDatosLocales = 0;
		if (decs) {
			tamDatosLocales = par1.getDireccion();
		}
		else {
			tamDatosLocales = datosh.getDireccion();
		}
		prologo(datosh.getNivel(), tamDatosLocales);
		etq = etq + longprologo;
		boolean err2 = Is(par1.getTs(), datosh.getNivel());
		epilogo(datosh.getNivel());
		emite("ir-ind");
		etq = etq + 1 + longepilogo;
		if (!coincide(TipoToken.llaveC)) {
			throw new Exception("Falta '}' en la posicion: " + getToken().getPosicion());
		}
		if (decs) {
			return new DatosBloque(inicio, par1.isError() || err2, par1.getNivel());
		}
		return new DatosBloque(inicio, par1.isError() || err2, datosh.getNivel());
	}

	// *****************************FIN DECLARACIONES************************
	// *****************************INSTRUCCIONES****************************
	/**
	 * Analiza sintacticamente todas las isntrucciones del programa. La primera es obligatoria (llamada interna a "I()"), el resto son opcionales.
	 * 
	 * @param tsph
	 * 
	 * @return Un booleano de error
	 * @throws Exception
	 *             Propaga las excepciones que hayan podido generar los métodos llamados
	 */
	private boolean Is(TablaSimbolos tsph, int nh) throws Exception {
		// Ins-> I RIns
		// creaCod();
		return RIs(I(tsph, nh), tsph, nh);
	}

	/**
	 * Analiza sintacticamente las instrucciones a partir de la primera (si las hubiera)
	 * 
	 * @param errh
	 *            El posible error de la primera instrucción (booleano)
	 * @return Un booleano de error
	 * @throws Exception
	 *             Si hay error sintáctico
	 */
	private boolean RIs(boolean errh, TablaSimbolos tsph, int nh) throws Exception {
		// RIs-> ; I RIs
		boolean errS = false;
		while (coincide(TipoToken.puntoycoma)) {
			boolean err2h = I(tsph, nh);
			if (errh || err2h)
				errS = true;
		}
		if (coincideOp0() || coincideCasting())
			throw new Exception("El operador '" + getToken().getLexema() + "' no asocia." + getToken().getPosicion());
		else
			if (coincide(TipoToken.EOF) || coincide(TipoToken.llaveC) || coincide(TipoToken.parC))
				return errS || errh;
		throw new Exception("Falta punto y coma al final de la instruccion. Linea: " + getToken().getLinea());
	}

	/**
	 * Analiza sintacticamente una única instrucción.
	 * 
	 * @return Un booleano de error
	 * @throws Exception
	 *             En caso de error sintáctico
	 */
	private boolean I(TablaSimbolos tsph, int nh) throws Exception {
		getNextToken();
		boolean errS = false;
		if (coincide(TipoToken.iden)) {
			Token ta = tokenActual;
			getNextToken();
			if (coincide(TipoToken.parA)) {
				errS = ICall(ta, tsph, nh);
			}
			else {
				tokenActual = ta;
				numTkn = listaTokens.indexOf(ta);
				errS = IAsig(tsph, nh);
			}
		}
		else
			if (coincide(TipoToken.insEsc))
				errS = IEscrit(tsph, nh);
			else
				if (coincide(TipoToken.insLee))
					errS = ILect(tsph, nh);
				else
					if (coincide(TipoToken.si))
						errS = iIF(tsph, nh);
					else
						if (coincide(TipoToken.desde))
							errS = iFor(tsph, nh);
						else
							if (coincide(TipoToken.mientras))
								errS = iWhile(tsph, nh);
							else
								if (coincide(TipoToken.reserva))
									errS = iNew(tsph, nh);
								else
									if (coincide(TipoToken.libera))
										errS = iDel(tsph, nh);
									else
										if (coincide(TipoToken.llaveA))
											errS = iComp(tsph, nh);
										else
											if (coincide(TipoToken.EOF))
												throw new Exception("Falta una instruccion en la linea " + getToken().getLinea());
											else
												throw new Exception("Identificador no valido en la linea: " + getToken().getLinea());
		return errS;
	}

	/**
	 * Analiza sintacticamente las instrucciones de tipo "Escribe" del programa
	 * 
	 * @return Un booleano de error
	 * @throws Exception
	 *             Si hay error sintáctico
	 */
	private boolean IEscrit(TablaSimbolos tsph, int nh) throws Exception {
		boolean errS = false;
		getNextToken();
		if (!coincide(TipoToken.parA))
			throw new Exception("Se esperaba un ( en la posicion :" + getToken().getPosicion());
		DatosExpresion d = E0(new ArrayList<Integer>(), new ArrayList<Integer>(), tsph, false, nh);
		parchea(d.getListaf(),d.getListav(),etq,etq);
		ExprTipo t = d.getExpr();		
		errS = (t.isError());
		if (errS)
			avanza(TipoToken.parC);
		if (!coincide(TipoToken.parC))
			throw new Exception("Se esperaba un ) en la posicion :" + getToken().getPosicion());
		emite("escribe");
		etq = etq + 1;
		getNextToken();
		return errS;
	}

	/**
	 * Analiza sintacticamente las instrucciones de tipo "Lee" del programa
	 * 
	 * @return Un booleano de error
	 * @throws Exception
	 *             Si hay error sintáctico
	 */
	private boolean ILect(TablaSimbolos tsph, int nh) throws Exception {
		boolean errS;
		getNextToken();
		if (!coincide(TipoToken.parA))
			throw new Exception("Se esperaba un ( en la posicion :" + getToken().getPosicion());
		getNextToken();
		Token aux = getToken();
		if (!coincide(TipoToken.iden))
			throw new Exception("Se esperaba un identificador en la posicion :" + getToken().getPosicion());
		getNextToken();
		if (!coincide(TipoToken.parC))
			throw new Exception("Se esperaba un ) en la posicion :" + getToken().getPosicion());
		if (!tsph.existeId(aux.getLexema(), nh)) {
			erroresC.add(new ErrorContextual("La variable " + aux.getLexema() + " no esta declarada." + aux.getPosicion() + "\n"));
			errS = true;
			return errS;
		}
		accesoVar(tsph.regDe(aux.getLexema(), nh));
		String op = codigo(tsph.tipoDe2(aux.getLexema(), nh));
		emite(op);
		etq = etq + 1;
		if (op.equals("lee-natural")) {
			emite("desapila-ind-natural");
		}
		else
			if (op.equals("lee-entero")) {
				emite("desapila-ind-entero");
			}
			else
				if (op.equals("lee-real")) {
					emite("desapila-ind-real");
				}
				else
					if (op.equals("lee-booleano")) {
						emite("desapila-ind-booleano");
					}
					else
						if (op.equals("lee-caracter")) {
							emite("desapila-ind-caracter");
						}
		etq = etq + longAccesoVar(tsph.regDe(aux.getLexema(), nh)) + 1;
		getNextToken();
		errS = false;
		return errS;
	}

	/**
	 * Analiza sintacticamente las instrucciones de asignación del programa
	 * 
	 * @return Un booleano de error
	 * @throws Exception
	 *             Si hay error sintáctico
	 */
	private boolean IAsig(TablaSimbolos tsph, int nh) throws Exception {
		boolean errS;
		Token aux = getToken();
		ExprTipo tInicial;
		ExprTipo t;
		int nVar = tsph.existeIdAnt(aux.getLexema(), nh);
		if (nVar == -1) {
			erroresC.add(new ErrorContextual("La variable " + aux.getLexema() + " no existe. Posicion: " + aux.getPosicion() + "\n"));
			errS = true;
			avanzaCierreInstruccion();
			return errS;
		}
		else {
			tInicial = tsph.exprDe(aux.getLexema(), nVar);
			t = D(tInicial, tsph, nVar);
			if (t.isError()) {
				avanzaCierreInstruccion();
				erroresC.add(new ErrorContextual("Ocurrio un error antes de la asignacion. Posicion:" + tokenActual.getPosicion() + "\n"));
				errS = true;
				return errS;
			}
			else {
				if (!coincide(TipoToken.asig))
					throw new Exception("Se esperaba un = en la posicion :" + getToken().getPosicion());
				DatosExpresion t1 = E0(new ArrayList<Integer>(), new ArrayList<Integer>(), tsph, false, nh);
				parchea(t1.getListaf(), t1.getListav(), etq, etq);
				if (t1.getExpr().getTipo() == Tipo.err) {
					erroresC.add(new ErrorContextual("Hubo un error en la expresion despues de la asignacion. Linea: " + aux.getLinea() + "\n"));
					errS = true;
					return errS;
				}
				ArrayList<ParTipos> v = new ArrayList<ParTipos>();
				if (t.getTipo() == Tipo.booleano && tablas.existeOperacion(Operadores.asignacion, tsph.tipo2Token(t.getTipo()), tsph.tipo2Token(t1.getExpr().getTipo()))) {
					emite("desapila-ind-booleano");
					etq = etq + 1;
				}else if (t.getTipo() == Tipo.car && tablas.existeOperacion(Operadores.asignacion, tsph.tipo2Token(t.getTipo()), tsph.tipo2Token(t1.getExpr().getTipo()))) {
					emite("desapila-ind-caracter");
					etq = etq + 1;
				}else if (t.getTipo() == Tipo.nat && tablas.existeOperacion(Operadores.asignacion, tsph.tipo2Token(t.getTipo()), tsph.tipo2Token(t1.getExpr().getTipo()))) {
					emite("desapila-ind-natural");
					etq = etq + 1;
				}else	if (t.getTipo() == Tipo.ent && tablas.existeOperacion(Operadores.asignacion, tsph.tipo2Token(t.getTipo()), tsph.tipo2Token(t1.getExpr().getTipo()))) {
					emite("desapila-ind-entero");
					etq = etq + 1;
				}else if (t.getTipo() == Tipo.real && tablas.existeOperacion(Operadores.asignacion, tsph.tipo2Token(t.getTipo()), tsph.tipo2Token(t1.getExpr().getTipo()))) {
					emite("desapila-ind-real");
					etq = etq + 1;
				}else if (t.getTipo() == Tipo.punt && tablas.existeOperacion(Operadores.asignacion, tsph.tipo2Token(t.getTipo()), tsph.tipo2Token(t1.getExpr().getTipo()))){
					emite("desapila-ind-nulo");
					etq = etq + 1;
				} else if(compatibles(t, t1.getExpr(), tsph, nh, v)){
					emite("mueve(" + t.getTamaño() + ")");
					etq = etq + 1;
				}else{
					erroresC.add(new ErrorContextual("La asignacion es incompatible por problemas de tipado. Linea: " + aux.getLinea() + "\n"));
					errS = true;
					return errS;
				}
			}
		}
		errS = false;
		return errS;
	}

	/**
	 * Analiza sintacticamente la parte izquierda de una asignacion
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Una clase del tipo ExprTipo que contiene informacion acerca del tipo de la expresion
	 * @throws Exception En caso de fallo sintactico
	 */
	private ExprTipo D(ExprTipo tInicial, TablaSimbolos tsph, int nh) throws Exception {
		Token aux = tokenActual;
		if (coincide(TipoToken.iden)) {
			if (tsph.existeId(aux.getLexema(), nh))
				if (tsph.claseDe(aux.getLexema(), nh) == TipoToken.variable || tsph.claseDe(aux.getLexema(), nh) == TipoToken.paramvar) {
					accesoVar(tsph.regDe(aux.getLexema(), nh));
					etq = etq + longAccesoVar(tsph.regDe(aux.getLexema(), nh));
					ExprTipo resp1 = ref(tsph, tsph.exprDe(aux.getLexema(), nh), nh);
					ExprTipo resp2 = RD(resp1, tsph, nh);
					return resp2;
				}
				else {
					erroresC.add(new ErrorContextual(aux.getLexema() + " no es una variable definida. Posicion: " + aux.getPosicion() + "\n"));
					return new ExprTipo(Tipo.err);
				}
			else {
				erroresC.add(new ErrorContextual("La variable " + aux.getLexema() + " no esta declarada. Posicion: " + aux.getPosicion() + "\n"));
				return new ExprTipo(Tipo.err);
			}
		}
		return new ExprTipo(Tipo.err);
	}
	
	/**
	 * Analiza sintacticamente la parte derecha de una asignacion
	 * @param t El objeto de tipo ExprTipo correspondiente a la parte izquierda de la asignacion
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Una clase del tipo ExprTipo que contiene informacion acerca del tipo de la expresion
	 * @throws Exception En caso de fallo sintactico
	 */
	private ExprTipo RD(ExprTipo t, TablaSimbolos tsph, int nh) throws Exception {
		Token aux = tokenActual;
		if (coincide(TipoToken.punto)) {
			if (t.getTipo() == Tipo.rec) {
				getNextToken();
				if (t.getCampos().existeId(tokenActual.getLexema())) {
					emite("apila-natural(" + t.getCampos().dameDespCampo(tokenActual.getLexema()) + ")");
					emite("suma");
					etq = etq + 2;
					Token aux2 = tokenActual;
					getNextToken();
					ExprTipo resp1 = ref(tsph, t.getCampos().dameInfoCampo(aux2.getLexema()), nh);
					ExprTipo resp2 = RD(resp1, tsph, nh);
					return resp2;
				}
				else {
					erroresC.add(new ErrorContextual("El campo '" + tokenActual.getLexema() + "' no pertenece al registro. Posicion: " + tokenActual.getPosicion() + "\n"));
					avanzaCierre();
					return new ExprTipo(Tipo.rec);
				}
			}
			else {
				erroresC.add(new ErrorContextual("Se esperaba un registro. Posicion: " + aux.getPosicion() + "\n"));
				avanzaCierre();
				return new ExprTipo(Tipo.err);
			}
		}
		else
			if (coincide(TipoToken.corA)) {
				if (t.getTipo() == Tipo.array) {
					boolean error = false;
					emite("apila-natural(" + t.getTipoBase().getTamaño() + ")");
					ExprTipo exp = E0(new ArrayList<Integer>(), new ArrayList<Integer>(), tsph, false, nh).getExpr();
					if (exp.getTipo() != Tipo.nat && exp.getTipo() != Tipo.ent) {
						erroresC.add(new ErrorContextual("El numero del elemento debe ser un natural. Posicion: " + aux.getPosicion() + "\n"));
						avanzaCierre();
						error = true;
					}
					if (!coincide(TipoToken.corC)) {
						throw new Exception("Se esperaba ']' en la posicion: " + tokenActual.getPosicion() + "\n");
					}
					emite("multiplica");
					emite("suma");
					etq = etq + 3;
					getNextToken();
					ExprTipo resp1 = ref(tsph, t.getTipoBase(), nh);
					ExprTipo resp2 = RD(resp1, tsph, nh);
					if (error) {
						resp2.setError(true);
					}
					return resp2;
				}
				else {
					erroresC.add(new ErrorContextual("Se esperaba un array. Posicion: " + aux.getPosicion() + "\n"));
					avanzaCierre();
					return new ExprTipo(Tipo.err);
				}
			}
			else
				if (coincide(TipoToken.flecha)) {
					if (t.getTipo() == Tipo.punt) {
						emite("apila-ind");
						etq = etq + 1;
						getNextToken();
						ExprTipo resp1 = ref(tsph, t.getTipoBase(), nh);
						ExprTipo resp2 = RD(resp1, tsph, nh);
						return resp2;
					}
					else {
						erroresC.add(new ErrorContextual("Se esperaba un puntero. Posicion:" + aux.getPosicion() + "\n"));
						return new ExprTipo(Tipo.punt);
					}
				}
				else
					if (coincide(TipoToken.iden)) {
						getNextToken();
						if (tsph.existeId(aux.getLexema(), nh)) {
							ExprTipo resp1 = ref(tsph, tsph.exprDe(aux.getLexema(), nh), nh);
							ExprTipo resp2 = RD(resp1, tsph, nh);
							return resp2;
						}
						else
							throw new Exception("Se produjo un error en el atributo: " + tokenActual.getPosicion());
					}
					else
						if (t.getTipo() == Tipo.booleano || t.getTipo() == Tipo.car || t.getTipo() == Tipo.ent || t.getTipo() == Tipo.nat || t.getTipo() == Tipo.real) {
							return new ExprTipo(t.getTipo(), t.getTamaño());
						}
						else
							if (coincideSeparador()) {
								return t;
							}
							else
								throw new Exception("Se produjo un error en el atributo: " + tokenActual.getPosicion());
	}

	/**
	 * Analiza sintacticamente un bloque de instrucciones
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Un booleano indicando si ha ocurrido un error
	 * @throws Exception En caso de error sintactico
	 */ 
	private boolean iComp(TablaSimbolos tsph, int nh) throws Exception {
		boolean errS = Is(tsph, nh);
		if (!coincide(TipoToken.llaveC))
			throw new Exception("Se esperaba un } en la posicion :" + getToken().getPosicion());
		getNextToken();
		return errS;
	}

	/**
	 * Analiza sintacticamente una instruccion de tipo If (condicional)
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Un booleano indicando si ha ocurrido un error
	 * @throws Exception En caso de error sintactico
	 */
	private boolean iIF(TablaSimbolos tsph, int nh) throws Exception {
		boolean errS;
		DatosCaso casos = Casos(tsph, nh);
		errS = casos.isErr();
		if (coincide(TipoToken.sino)) {
			int etqaux2 = etq;
			if (casos.getListaSaltos() == null) {
				emite("ir-a(?)");
				etq = etq + 1;
			}
			boolean errSino = PElse(tsph, nh);
			errS = errS || errSino;
			if (!errS)
				parchea(etqaux2, etq);
		}
		if (casos.getListaSaltos() != null) {
			for (int i = 0; i < casos.getListaSaltos().size(); i++) {
				parchea(casos.getListaSaltos().get(i), etq);
			}
		}
		if (!coincide(TipoToken.finsi)) {
			throw new Exception("Se esperaba un 'finsi' tras la linea:" + getToken().getLinea());
		}
		getNextToken();
		return errS;
	}

	/**
	 * Analiza sintacticamente las condiciones booleanas de una instruccion de tipo If (condicional)
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Una clase de tipo DatosCaso que contiene informacion de las condiciones y sus posibles errores
	 * @throws Exception En caso de error sintactico
	 */
	private DatosCaso Casos(TablaSimbolos tsph, int nh) throws Exception {
		ArrayList<Integer> saltos = new ArrayList<Integer>();
		DatosCaso aux = Caso(tsph, saltos, nh);
		return RCasos(aux.isErr(), aux.getListaSaltos(), tsph, nh);
	}

	/**
	 * Analiza sintacticamente las condiciones booleanas despues de la primera en una instruccion de tipo If (condicional)
	 * @param errh Si ha habido errores anteriormente
	 * @param saltosh La lista de saltos
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Una clase de tipo DatosCaso que contiene informacion de las condiciones y sus posibles errores
	 * @throws Exception Exception En caso de error sintactico
	 */
	private DatosCaso RCasos(boolean errh, ArrayList<Integer> saltosh, TablaSimbolos tsph, int nh) throws Exception {
		// RCasos ::= obien Caso RCasos
		// RCasos ::= lambda
		boolean errS = errh;
		DatosCaso aux = null;
		ArrayList<Integer> saltosS = saltosh;
		while (coincide(TipoToken.obien)) {
			aux = Caso(tsph, saltosS, nh);
			saltosS = aux.getListaSaltos();
			errS = errS || aux.isErr();
		}
		return new DatosCaso(errS, saltosS);
	}
	
	/**
	 * Analiza sintacticamente la primera condicion booleana en una instruccion de tipo If (condicional)
	 * @param saltos La lista de saltos
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Una clase de tipo DatosCaso que contiene informacion de las condiciones y sus posibles errores
	 * @throws Exception Exception En caso de error sintactico
	 */
	private DatosCaso Caso(TablaSimbolos tsph, ArrayList<Integer> saltos, int nh) throws Exception {
		Token aux = tokenActual;
		boolean errS = false;
		DatosExpresion e = E0(new ArrayList<Integer>(), new ArrayList<Integer>(), tsph, false, nh);
		if (e == null) {
			throw new Exception("Se esperaba una expresion valida. Posicion: " + tokenActual.getPosicion());
		}
		if (e.getExpr().getTipo() != Tipo.booleano) {
			erroresC.add(new ErrorContextual("La condicion debe ser una expresion booleana en el 'obien' que comienza en la posicion " + aux.getPosicion()));
			avanza(TipoToken.entonces);
			errS = true;
		}
		parchea(e.getListaf(), e.getListav(), etq, etq);
		emite("ir-f(?)");
		int etqaux = etq;
		etq = etq + 1;
		if (!coincide(TipoToken.entonces)) {
			throw new Exception("Se esperaba palabra reservada 'entonces'. Posicion: " + tokenActual.getPosicion());
		}
		boolean errI = I(tsph, nh);
		errS = errS || errI;
		emite("ir-a(?)");
		saltos.add(etq);
		etq = etq + 1;
		parchea(etqaux, etq);
		return new DatosCaso(errS, saltos);
	}

	/**
	 * Analiza sintacticamente el caso "sino" de una instruccion de tipo If (condicional)
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Un booleano indicando si ha habido fallo
	 * @throws Exception Exception En caso de error sintactico
	 */
	private boolean PElse(TablaSimbolos tsph, int nh) throws Exception {
		boolean errS = I(tsph, nh);
		return errS;
	}
	
	/** 
	 * Analiza sintacticamente el contenido de una instruccion de tipo For (bucle)
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Un booleano indicando si ha habido fallo
	 * @throws Exception En caso de error sintactico
	 */
	private boolean iFor(TablaSimbolos tsph, int nh) throws Exception {
		boolean errS;
		getNextToken();
		Token aux = getToken();
		if (!coincide(TipoToken.iden))
			throw new Exception("Se esperaba un identificador en la posicion :" + getToken().getPosicion());
		errS = IAsig(tsph, nh);
		if (!coincide(TipoToken.hasta))
			throw new Exception("Se esperaba palabra reservada 'hasta' en la posicion :" + getToken().getPosicion());
		ExprTipo t3 = E0(new ArrayList<Integer>(), new ArrayList<Integer>(), tsph, false, nh).getExpr();
		int tmpetq = etq;
		emite("copia");
		accesoVar(tsph.regDe(aux.getLexema(), nh));
		emite("apila-ind");
		emite("mayoroigual");
		emite("ir-f(?)");
		etq = etq + 4 + longAccesoVar(tsph.regDe(aux.getLexema(), nh));
		if (t3.getTipo() == Tipo.err) {
			erroresC.add(new ErrorContextual("Hubo un error en la expresion despues de la asignacion. Linea: " + aux.getLinea() + "\n"));
			errS = true;
			return errS;
		}
		if (!coincide(TipoToken.hacer))
			throw new Exception("Se esperaba palabra reservada 'hacer' en la posicion :" + getToken().getPosicion());
		errS = I(tsph, nh);
		accesoVar(tsph.regDe(aux.getLexema(), nh));
		emite("copia");
		emite("apila-ind");
		emite("apila-natural(1)");
		emite("suma");
		emite("desapila-ind-natural");
		emite("ir-a(" + tmpetq + ")");
		parchea(tmpetq + 3 + longAccesoVar(tsph.regDe(aux.getLexema(), nh)), etq + 6 + longAccesoVar(tsph.regDe(aux.getLexema(), nh)));
		emite("desapila");
		etq = etq + 7 + longAccesoVar(tsph.regDe(aux.getLexema(), nh));
		if (coincide(TipoToken.llaveC))
			getNextToken();
		return errS;
	}

	/** 
	 * Analiza sintacticamente el contenido de una instruccion de tipo While (bucle)
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Un booleano indicando si ha habido fallo
	 * @throws Exception En caso de error sintactico
	 */
	private boolean iWhile(TablaSimbolos tsph, int nh) throws Exception {
		int etqaux1 = etq;
		boolean errS;
		Token aux = getToken();
		DatosExpresion t1 = E0(new ArrayList<Integer>(), new ArrayList<Integer>(), tsph, false, nh);
		if (t1.getExpr().getTipo() == Tipo.err) {
			erroresC.add(new ErrorContextual("Hubo un error en la expresion. Linea: " + aux.getLinea() + "\n"));
			errS = true;
			return errS;
		}
		if (!coincide(TipoToken.hacer))
			throw new Exception("Se esperaba palabra reservada 'hacer' en la posicion :" + getToken().getPosicion());
		parchea(t1.getListaf(), t1.getListav(), etq, etq);
		emite("ir-f(?)");
		int etqaux2 = etq;
		etq = etq + 1;
		errS = I(tsph, nh);
		emite("ir-a(" + etqaux1 + ")");
		etq = etq + 1;
		parchea(etqaux2, etq);
		return errS;
	}

	/** 
	 * Analiza sintacticamente el contenido de una instruccion de tipo New (asignacion de memoria)
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Un booleano indicando si ha habido fallo
	 * @throws Exception En caso de error sintactico
	 */
	private boolean iNew(TablaSimbolos tsph, int nh) throws Exception {
		getNextToken();
		Token aux = tokenActual;
		int nVar = tsph.existeIdAnt(aux.getLexema(), nh);
		if (nVar != -1) {
			ExprTipo tInicial = tsph.exprDe(aux.getLexema(), nVar);
			ExprTipo aux2 = D(tInicial, tsph, nVar);
			String s = "";
			if (aux2.getTipo() == Tipo.punt) {
				if (aux2.getTipoBase().getTipo() == Tipo.ref) {
					int nTipoBase = tsph.existeIdAnt(aux2.getTipoBase().getId(), nVar);
					s += tsph.exprDe(aux2.getTipoBase().getId(), nTipoBase).getTamaño();
				}
				else {
					s += aux2.getTipoBase().getTamaño();
				}
				emite("new(" + s + ")");
				emite("desapila-ind-natural");
				etq = etq + 2;
				return false;
			}
			else {
				erroresC.add(new ErrorContextual("El tipo de la variable ha de ser puntero. Linea: " + aux.getLinea() + "\n"));
				return true;
			}
		}
		erroresC.add(new ErrorContextual("La variable '" + aux.getLexema() + "' no ha sido definida correctamente. Posicion: " + aux.getPosicion() + "\n"));
		avanzaCierreInstruccion();
		return true;
	}
	
	/** 
	 * Analiza sintacticamente el contenido de una instruccion de tipo Del (borrado de memoria)
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Un booleano indicando si ha habido fallo
	 * @throws Exception En caso de error sintactico
	 */
	private boolean iDel(TablaSimbolos tsph, int nh) throws Exception {
		getNextToken();
		Token aux = tokenActual;
		int nVar = tsph.existeIdAnt(aux.getLexema(), nh);
		if (nVar != -1) {
			ExprTipo tInicial = tsph.exprDe(aux.getLexema(), nVar);
			ExprTipo aux2 = D(tInicial, tsph, nVar);
			String s = "";
			if (aux2.getTipo() == Tipo.punt) {
				if (aux2.getTipoBase().getTipo() == Tipo.ref) {
					int nTipoBase = tsph.existeIdAnt(aux2.getTipoBase().getId(), nVar);
					s += tsph.exprDe(aux2.getTipoBase().getId(), nTipoBase).getTamaño();
				}
				else {
					s += aux2.getTipoBase().getTamaño();
				}
				emite("apila-ind");
				emite("del(" + s + ")");
				etq = etq + 2;
				return false;
			}
			else {
				erroresC.add(new ErrorContextual("El tipo de la variable ha de ser puntero. Linea: " + aux.getLinea() + "\n"));
				return true;
			}
		}
		erroresC.add(new ErrorContextual("La variable '" + aux.getLexema() + "' no ha sido definida correctamente. Posicion: " + aux.getPosicion() + "\n"));
		avanzaCierreInstruccion();
		return true;
	}
	
	/**
	 * Analiza sintacticamente el contenido de una instruccion de llamada a procedimiento
	 * @param t El token que contiene la informacion del nombre del procedimiento (el inmediatamente anterior)
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @return Un booleano indicando si ha habido fallo
	 * @throws Exception En caso de error sintactico
	 */
	private boolean ICall(Token t, TablaSimbolos tsph, int nh) throws Exception {
		boolean errS = false;
		boolean aux = false;
		if (!coincide(TipoToken.parA)) {
			throw new Exception("Se esperaba '(' en la posicion: " + tokenActual.getPosicion());
		}
		int nProc = tsph.existeId(t.getLexema());
		if (nProc == -1) {
			erroresC.add(new ErrorContextual("El procedimiento '" + t.getLexema() + "' no esta declarado correctamente o no esta declarado. " + "Pos: " + t.getPosicion() + "\n"));
			avanza(TipoToken.parC);
			getNextToken();
			errS = true;
		}
		else
			if (tsph.claseDe(t.getLexema(), nProc) != TipoToken.procedimiento) {
				erroresC.add(new ErrorContextual("'" + t.getLexema() + "' no es un procedimiento valido. " + "Pos: " + t.getPosicion() + "\n"));
				errS = true;
			}
			else {
				apilaRet(0);
				int etqaux = etq + 3;
				etq = etq + longApilaRet;
				aux = AParams(tsph, tsph.exprDe(t.getLexema(), nProc).getParams(), nh);
				emite("ir-a(" + tsph.inicioDe(t.getLexema(), nProc) + ")");
				etq = etq + 1;
				parchea(etqaux, etq);
				if (!coincide(TipoToken.parC)) {
					throw new Exception("Se esperaba ')' en la posicion: " + tokenActual.getPosicion());
				}
				getNextToken();
			}
		return errS || aux;
	}

	/**
	 * Analiza sintacticamente los parametros de la llamada al procedimiento
	 * @param tsph La tabla de simbolos
	 * @param listah La lista de parametros a comprobar
	 * @param nh El nivel
	 * @return Un booleano indicando si ha habido fallo
	 * @throws Exception En caso de error sintactico
	 */
	private boolean AParams(TablaSimbolos tsph, ListaParams listah, int nh) throws Exception {
		boolean errS = false;
		Token ta = tokenActual;
		getNextToken();
		if (!coincide(TipoToken.parC)) {
			numTkn--;
			inicioPaso();
			etq = etq + longInicioPaso;
			DatosICall aux = LAParams(tsph, listah, nh);
			if (aux.isError()) {
				errS = true;
			}
			if (listah.getLista().size() != aux.getNumParams()) {
				erroresC.add(new ErrorContextual("El numero de parametros de la llamada al procedimiento es incorrecto. Posicion: " + ta.getPosicion() + "\n"));
				errS = true;
			}
			emite(finPaso);
			etq = etq + longFinPaso;
		}
		else
			if (listah.getLista().size() > 0) {
				erroresC.add(new ErrorContextual("Faltan parametros en la llamada al procedimiento. Posicion: " + ta.getPos() + "\n"));
				errS = true;
			}
		return errS;
	}

	/**
	 * Analiza sintacticamente el primer parametro de la llamada al procedimiento
	 * @param tsph La tabla de simbolos
	 * @param listah La lista de parametros a comprobar
	 * @param nh El nivel
	 * @return Un objeto de tipo DatosICall que contiene informacion relativa a los parametros de la llamada a procedimiento
	 * @throws Exception En caso de error sintactico
	 */
	private DatosICall LAParams(TablaSimbolos tsph, ListaParams listah, int nh) throws Exception {
		boolean errS = false;
		if (listah.getLista().size() == 0) {
			erroresC.add(new ErrorContextual("El numero de parametros es incorrecto\n"));
			errS = true;
		}
		emite("copia");
		boolean parh = true;
		Token ta = tokenActual;
		etq = etq + 1;
		DatosExpresion e1 = E0(new ArrayList<Integer>(), new ArrayList<Integer>(), tsph, parh, nh);
		parchea(e1.getListaf(),e1.getListav(),etq,etq);
		pasoParametro(e1.getModo(), listah.getLista().get(0));
		etq = etq + longPasoParametro(e1.getModo(), listah.getLista().get(0));
		int nparamsS = 1;
		if (e1.getExpr().getTipo() == Tipo.err) {
			erroresC.add(new ErrorContextual("Hubo un error en la evaluacion de la expresion del campo " + (nparamsS - 1) + ". No se pudo realizar la llamada. Posicion: " + ta.getPosicion()
					+ "\n"));
			errS = true;
		}
		if (listah.getLista().get(0).getModo().equals("variable") && e1.getModo().equals("valor")) {
			erroresC.add(new ErrorContextual("Se requiere una variable en el campo " + nparamsS + ". Posicion: " + ta.getPosicion() + "\n"));
			errS = true;
		}
		ArrayList<ParTipos> v = new ArrayList<ParTipos>();
		if (!compatibles(e1.getExpr(), listah.getLista().get(0).getTipo(), tsph, nh, v)) {
			erroresC.add(new ErrorContextual("Los tipos del procedimiento y la llamada son incompatibles. Posicion:" + ta.getPosicion() + "\n"));
			errS = true;
		}
		DatosICall resp = RLAParams(tsph, nparamsS, listah, nh);
		return new DatosICall(resp.getNumParams(), errS || resp.isError());
	}

	/**
	 * Analiza sintacticamente el primer parametro de la llamada al procedimiento
	 * @param tsph La tabla de simbolos
	 * @param nparamsh El numero de parametros a analizar
	 * @param listah La lista de parametros a comprobar
	 * @param nh El nivel
	 * @return Un objeto de tipo DatosICall que contiene informacion relativa a los parametros de la llamada a procedimiento
	 * @throws Exception En caso de error sintactico
	 */
	private DatosICall RLAParams(TablaSimbolos tsph, int nparamsh, ListaParams listah, int nh) throws Exception {
		int nparamsS = nparamsh;
		boolean errS = false;
		while (coincide(TipoToken.coma)) {
			nparamsS = nparamsS + 1;
			emite("copia");
			direccionParFormal(listah.getLista().get(nparamsS - 1));
			etq = etq + 1 + longDireccionParFormal(listah.getLista().get(nparamsS - 1));
			Token ta = tokenActual;
			DatosExpresion e2 = E0(new ArrayList<Integer>(), new ArrayList<Integer>(), tsph, true, nh);
			parchea(e2.getListaf(),e2.getListav(),etq,etq);
			if (e2.getExpr().getTipo() == Tipo.err) {
				erroresC.add(new ErrorContextual("Hubo un error en la evaluacion de la expresion del campo " + (nparamsS - 1) + ". No se pudo realizar la llamada. Posicion: "
						+ ta.getPosicion() + "\n"));
				errS = true;
			}
			else
				if (nparamsS > listah.getLista().size()) {
					erroresC.add(new ErrorContextual("Demasiados parametros en la llamada al procedimiento. Posicion: " + ta.getPosicion() + "\n"));
					errS = true;
				}
			if (listah.getLista().get(nparamsS - 1).getModo().equals("variable") && e2.getModo().equals("valor")) {
				erroresC.add(new ErrorContextual("Se requiere una variable en el campo " + (nparamsS) + ". Posicion: " + ta.getPosicion() + "\n"));
				errS = true;
			}
			ArrayList<ParTipos> v = new ArrayList<ParTipos>();
			if (!compatibles(listah.getLista().get(nparamsS - 1).getTipo(), e2.getExpr(), tsph, nh, v)) {
				erroresC.add(new ErrorContextual("El tipo de la llamada es incorrecto. Posicion: " + ta.getPosicion() + "\n"));
				errS = true;
			}
			pasoParametro(e2.getModo(), listah.getLista().get(nparamsS - 1));
			etq = etq + longPasoParametro(e2.getModo(), listah.getLista().get(nparamsS - 1));
		}
		return new DatosICall(nparamsS, errS);
	}

	// *****************************FIN INSTRUCCIONES************************
	// *****************************EXPRESIONES******************************
	/**
	 * Analiza sintacticamente las expresiones de menor prioridad del programa. Estas expresiones corresponden a los tipos de operaciones booleanas comparativas
	 * 
	 * @return El tipo (TipoToken) de la expresion
	 * @throws Exception
	 *             Si hay error sintáctico
	 */
	
	private DatosExpresion E0(ArrayList<Integer> lf, ArrayList<Integer> lv, TablaSimbolos tsph, boolean parh, int nh) throws Exception {
		DatosExpresion t1h;
		if (hayOp0()) {
			t1h = E1(lf, lv, tsph, false, nh);
		}
		else {
			t1h = E1(lf, lv, tsph, parh, nh);
		}
		DatosExpresion t1s;
		DatosExpresion t2h;
		Token ta = tokenActual;
		int etqaux = etq;
		if (coincideOp0()) {
			if (t1h == null) {
				throw new Exception("Expresion invalida antes del operador. Posicion: " + tokenActual.getPosicion());
			}
			t2h = E1(t1h.getListaf(), t1h.getListav(), tsph, false, nh);
			if (t1h.getExpr().getTipo() == Tipo.err || t2h.getExpr().getTipo() == Tipo.err) {
				t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "");
			}
			else
				if (!tablas.existeOperacion(Operadores.booleanoComp, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()))) {
					erroresC.add(new ErrorContextual("Error de tipado: No se puede comparar entre " + t1h.toString() + " y " + t2h.toString() + ". Posicion: " + ta.getPosicion() + "\n"));
					t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "");
				}
				else {
					// Exp.cod = parchea(ExpS0.cod, ExpS0.listaf, ExpS0.listav,ExpS0.etq, ExpS0.etq) ||parchea(ExpS1.cod, ExpS1.listaf, ExpS1.listav,ExpS1.etq, ExpS1.etq) || OpComp.op
					parchea(t1h.getListaf(), t1h.getListav(), etqaux, etqaux);
					parchea(t2h.getListaf(), t2h.getListav(), etq, etq);
					String op = codigo(ta.getTipo());
					emite(op);
					etq = etq + 1;
					TipoToken tToken = tablas.getTipoResultado(Operadores.booleanoComp, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()));
					Tipo tipo = token2Tipo(tToken);
					t1s = new DatosExpresion(new ExprTipo(tipo, 1), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
				}
		}
		else {
			t1s = t1h;
		}
		return t1s;
	}

	/**
	 * Analiza sintacticamente las expresiones de prioridad 1 del programa. Estas expresiones corresponden a los tipos de operaciones aditivas.
	 * 
	 * @return El tipo (TipoToken) de la expresion
	 * @throws Exception
	 *             Propaga las posibles excepciones que hayan podido lanzar los métodos llamados
	 */
	private DatosExpresion E1(ArrayList<Integer> lf, ArrayList<Integer> lv, TablaSimbolos tsph, boolean parh, int nh) throws Exception {
		DatosExpresion t1h;
		if (hayOp1()) {
			t1h = E2(lf, lv, tsph, false, nh);
		}
		else {
			t1h = E2(lf, lv, tsph, parh, nh);
		}
		DatosExpresion t1s = RE1(t1h, tsph, false, nh);
		return t1s;
	}

	/**
	 * Analiza sintacticamente el resto de las expresiones de prioridad 1 del programa. Estas expresiones corresponden a los tipos de operaciones aditivas
	 * 
	 * @param t1h
	 *            El tipo de la expresión que opera con esta
	 * @return El tipo (TipoToken) de la expresion
	 * @throws Exception
	 *             Si hay error sintáctico
	 */
	private DatosExpresion RE1(DatosExpresion t1h, TablaSimbolos tsph, boolean parh, int nh) throws Exception {
		DatosExpresion t2h;
		DatosExpresion t1s = t1h;
		boolean salto = false;
		int etqaux = 0;
		while (coincideOp1()) {
			if (t1s == null) {
				throw new Exception("Expresion invalida antes del operador. Posicion: " + tokenActual.getPosicion());
			}
			if (coincide(TipoToken.or)) {
				parchea(t1s.getListaf(), null, etq + 2, -1);
				emite("copia");
				emite("ir-v(?)");
				emite("desapila");
				etqaux = etq + 1;
				etq = etq + 3;
				salto = true;
			}
			Token ta = tokenActual;
			int etqaux2 = etq;
			t2h = E2(new ArrayList<Integer>(), new ArrayList<Integer>(), tsph, parh, nh);
			if (salto) {
				parchea(t2h.getListaf(), null, etq, -1);
			}
			t1h = t1s;
			if (t1h.getExpr().getTipo() == Tipo.err || t2h.getExpr().getTipo() == Tipo.err || t1h.getExpr().isError() || t2h.getExpr().isError()) {
				t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "");
			}
			else
				if (!tablas.existeOperacion(Operadores.aditivo, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()))
						&& !tablas.existeOperacion(Operadores.or, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()))) {
					erroresC.add(new ErrorContextual("Error de tipado: No se puede operar entre '" + t1h.getExpr().getTipo() + "' y '" + t2h.getExpr().getTipo() + "' con el operador '"
							+ ta.getLexema() + "'. Posicion: " + ta.getPosicion() + "\n"));
					t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
				}
				else {
					if (tablas.getTipoResultado(Operadores.aditivo, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo())) == null) {
						TipoToken tToken = tablas.getTipoResultado(Operadores.or, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()));
						Tipo tipo = token2Tipo(tToken);
						ArrayList<Integer> aux = t1s.getListav();
						aux.addAll(t2h.getListav());
						aux.add(etqaux);
						// ExpS0.listav = ExpS1.listav ++ Term.listav ++ [ExpS1.etq + 1]
						// ExpS0.listaf = []
						t1s = new DatosExpresion(new ExprTipo(tipo, 1), new ArrayList<Integer>(), aux, "");
					}
					else {
						// ExpS0.cod = parchea(ExpS1.cod,ExpS1.listaf,ExpS1.listav,ExpS1.etq,ExpS1.etq) ||parchea(Term.cod,Term.listaf,Term.listav,Term.etq,Term.etq) || OpAd.op
						// ExpS0.listaf = ExpS0.listav = []
						parchea(t1s.getListaf(), t1s.getListav(), etqaux2, etqaux2);
						parchea(t2h.getListaf(), t2h.getListav(), etq, etq);
						String op = codigo(ta.getTipo());
						emite(op);
						etq = etq + 1;
						TipoToken tToken = tablas.getTipoResultado(Operadores.aditivo, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()));
						Tipo tipo = token2Tipo(tToken);
						t1s = new DatosExpresion(new ExprTipo(tipo, 1), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
					}
				}
			salto = false;
		}
		return t1s;
	}

	/**
	 * Analiza sintacticamente las expresiones de prioridad 2 del programa. Estas expresiones corresponden a los tipos de operaciones multiplicativas.
	 * 
	 * @return El tipo (TipoToken) de la expresion
	 * @throws Exception
	 *             Propaga las posibles excepciones que hayan podido lanzar los métodos llamados
	 */
	private DatosExpresion E2(ArrayList<Integer> lf, ArrayList<Integer> lv, TablaSimbolos tsph, boolean parh, int nh) throws Exception {
		DatosExpresion t1h;
		if (hayOp2()) {
			t1h = E3(lf, lv, tsph, false, nh);
		}
		else {
			t1h = E3(lf, lv, tsph, parh, nh);
		}
		DatosExpresion t1s = RE2(t1h, tsph, parh, nh);
		return t1s;
	}

	/**
	 * Analiza sintacticamente el resto de las expresiones de prioridad 2 del programa. Estas expresiones corresponden a los tipos de operaciones multiplicativas.
	 * 
	 * @param t1h
	 *            El tipo de la expresion que opera con esta
	 * @return El tipo (TipoToken) de la expresion
	 * @throws Exception
	 *             En caso de errores sintacticos
	 */
	private DatosExpresion RE2(DatosExpresion t1h, TablaSimbolos tsph, boolean parh, int nh) throws Exception {
		DatosExpresion t2h;
		DatosExpresion t1s = t1h;
		int etqaux = 0;
		boolean salto = false;
		while (coincideOp2()) {
			if (t1s == null) {
				throw new Exception("Expresion invalida antes del operador. Posicion: " + tokenActual.getPosicion());
			}
			if (coincide(TipoToken.and)) {
				// parchea(Term1.cod, [], Term1.listav,?, Term1.etq + 2)
				parchea(null, t1s.getListav(), -1, etq + 2);
				emite("copia");
				emite("ir-f(?)");
				emite("desapila");
				etqaux = etq + 1;
				etq = etq + 3;
				salto = true;
			}
			Token ta = getToken();
			int etqaux2 = etq;
			t2h = E3(new ArrayList<Integer>(), new ArrayList<Integer>(), tsph, parh, nh);
			if (salto)
				// parchea(Fact.cod, [], Fact.listav, ?, Fact.etq)
				parchea(null, t2h.getListav(), -1, etq);
			t1h = t1s;
			String op2 = "";
			if (t1h.getExpr().getTipo() == Tipo.err || t2h.getExpr().getTipo() == Tipo.err) {
				t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "");
			}
			else
				if (!tablas.existeOperacion(Operadores.multiplicativo, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()))
						&& !tablas.existeOperacion(Operadores.and, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()))
						&& !tablas.existeOperacion(Operadores.modulo, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()))) {
					erroresC.add(new ErrorContextual("Error de tipado: No se puede operar entre '" + t1h.getExpr().getTipo() + "' y '" + t2h.getExpr().getTipo() + "' con el operador '"
							+ ta.getLexema() + "'. Posicion: " + ta.getPosicion() + "\n"));
					t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
				}
				else {
					op2 = codigo(ta.getTipo());
					if (tablas.existeOperacion(Operadores.multiplicativo, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()))
							&& (op2.equals("multiplica") || op2.equals("divide"))) {
						/*parchea(Term1.cod, Term1.listaf, Term1.listav,Term1.etq, Term1.etq) ||parchea(Fact.cod, Fact.listaf, Fact.listav,Fact.etq, Fact.etq) || OpMul.op
						Termo.listaf = Termo.listav = []*/
						parchea(t1s.getListaf(), t1s.getListav(), etqaux2, etqaux2);
						parchea(t2h.getListaf(), t2h.getListav(), etq, etq);
						emite(op2);
						etq = etq + 1;
						TipoToken tToken = tablas.getTipoResultado(Operadores.multiplicativo, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()));
						Tipo tipo = token2Tipo(tToken);
						t1s = new DatosExpresion(new ExprTipo(tipo, 1), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
					}
					else
						if (tablas.existeOperacion(Operadores.modulo, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo())) && op2.equals("modulo")) {
							/*parchea(Term1.cod, Term1.listaf, Term1.listav,Term1.etq, Term1.etq) ||parchea(Fact.cod, Fact.listaf, Fact.listav,Fact.etq, Fact.etq) || OpMul.op
							Termo.listaf = Termo.listav = []*/
							parchea(t1s.getListaf(), t1s.getListav(), etqaux2, etqaux2);
							parchea(t2h.getListaf(), t2h.getListav(), etq, etq);
							emite(op2);
							etq = etq + 1;
							TipoToken tToken = tablas.getTipoResultado(Operadores.modulo, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()));
							Tipo tipo = token2Tipo(tToken);
							t1s = new DatosExpresion(new ExprTipo(tipo, 1), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
						}
						else {
							// Term0.listf = Term1.listaf ++ Fact.listaf ++ [Term1.etq + 1]
							// Term0.listav = []
							TipoToken tToken = tablas.getTipoResultado(Operadores.and, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()));
							Tipo tipo = token2Tipo(tToken);
							ArrayList<Integer> aux = t1s.getListaf();
							aux.addAll(t2h.getListaf());
							aux.add(etqaux);
							t1s = new DatosExpresion(new ExprTipo(tipo, 0), aux, new ArrayList<Integer>(), "valor");
						}
				}
			salto = false;
		}
		return t1s;
	}

	/**
	 * Analiza sintacticamente las expresiones de prioridad 3 del programa. Estas expresiones corresponden a los tipos de operaciones desplazadoras.
	 * 
	 * @return El tipo (TipoToken) de la expresion
	 * @throws Exception
	 *             Si hay error sintáctico
	 */
	private DatosExpresion E3(ArrayList<Integer> lf, ArrayList<Integer> lv, TablaSimbolos tsph, boolean parh, int nh) throws Exception {
		DatosExpresion t1h;
		if (hayOp3()) {
			t1h = E4(lf, lv, tsph, false, nh);
		}
		else {
			t1h = E4(lf, lv, tsph, parh, nh);
		}
		DatosExpresion t1s;
		DatosExpresion t2h;
		int etqaux;
		while (coincideOp3()) {
			if (t1h == null) {
				throw new Exception("Expresion invalida antes del operador. Posicion: " + tokenActual.getPosicion());
			}
			etqaux = etq;
			Token ta = getToken();
			t2h = E3(t1h.getListaf(), t1h.getListav(), tsph, false, nh);
			if (t2h.getExpr().getTipo() == Tipo.err || t1h.getExpr().getTipo() == Tipo.err) {
				t1h = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
			}
			else
				if (!tablas.existeOperacion(Operadores.desplazadores, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()))) {
					erroresC.add(new ErrorContextual("Error de tipado: No se puede operar entre '" + t1h.getExpr().getTipo() + "' y '" + t2h.getExpr().getTipo() + "' con el operador '"
							+ ta.getLexema() + "'. Posicion: " + ta.getPosicion() + "\n"));
					t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
				}
				else {
					// E30.cod = parchea(E4.cod, E4.listaf, E4.listav,E4.etq,E4.etq) || parchea(E31.cod, E31.listaf, E31.listav,E31.etq, E31.etq) || Op3.op
					parchea(t1h.getListaf(), t1h.getListav(), etqaux, etqaux);
					parchea(t2h.getListaf(), t2h.getListav(), etq, etq);
					String op = codigo(ta.getTipo());
					emite(op);
					etq = etq + 1;
					TipoToken tToken = tablas.getTipoResultado(Operadores.desplazadores, tsph.tipo2Token(t1h.getExpr().getTipo()), tsph.tipo2Token(t2h.getExpr().getTipo()));
					Tipo tipo = token2Tipo(tToken);
					t1h = new DatosExpresion(new ExprTipo(tipo, 1), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
				}
		}
		t1s = t1h;
		return t1s;
	}

	/**
	 * Analiza sintacticamente las expresiones de prioridad 4 del programa. Estas expresiones corresponden a los tipos de operaciones unarias.
	 * 
	 * @return El tipo (TipoToken) de la expresion
	 * @throws Exception
	 *             Si hay error sintáctico
	 */
	private DatosExpresion E4(ArrayList<Integer> lf, ArrayList<Integer> lv, TablaSimbolos tsph, boolean parh, int nh) throws Exception {
		boolean hayOp4 = false;
		DatosExpresion t1s = null;
		DatosExpresion t1h;
		getNextToken();
		String op4 = "";
		String op4b = "";
		int numOp4 = 0;
		if (coincideOp4()) {
			hayOp4 = true;
			Token token = getToken();
			if (token.getTipo() == TipoToken.resta)
				token.setTipo(TipoToken.unario);
			op4 = codigo(token.getTipo());
			while (token.getTipo() == getToken().getTipo()) {
				numOp4++;
				getNextToken();
			}
		}
		if (coincide(TipoToken.parA)) {
			Token ta = getToken();
			getNextToken();
			if (coincideOp4b()) {
				if (coincide(TipoToken.tReal))
					getToken().setTipo(TipoToken.castReal);
				op4b = codigo(getToken().getTipo());
				getNextToken();
				if (!coincide(TipoToken.parC))
					throw new Exception("Se esperaba parentesis cerrado en la posicion :" + getToken().getPosicion());
				getNextToken();
				Token aux = tokenActual;
				int t = numTkn;
				if (coincide(TipoToken.parA)) {
					getNextToken();
					if (coincideOp4b()) {
						erroresC.add(new ErrorContextual("Los castings no asocian. Posicion :" + tokenActual.getPosicion() + "\n"));
						t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "");
						avanza(TipoToken.puntoycoma);
						return t1s;
					}
				}
				tokenActual = aux;
				numTkn = t;
				t1h = E5(lf, lv, tsph, false, nh);
				TipoToken aux3;
				Tipo tipo3;
				if (t1h.getExpr().getTipo() == Tipo.err) {
					t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
				}
				else
					if (tablas.existeOperacion(Operadores.castingC, tsph.tipo2Token(t1h.getExpr().getTipo()), null) && op4b.equals("castcar")) {
						aux3 = tablas.getTipoResultado(Operadores.castingC, tsph.tipo2Token(t1h.getExpr().getTipo()), null);
						tipo3 = token2Tipo(aux3);
						t1s = new DatosExpresion(new ExprTipo(tipo3, 1), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
					}
					else
						if (tablas.existeOperacion(Operadores.castingR, tsph.tipo2Token(t1h.getExpr().getTipo()), null) && op4b.equals("castreal")) {
							aux3 = tablas.getTipoResultado(Operadores.castingR, tsph.tipo2Token(t1h.getExpr().getTipo()), null);
							tipo3 = token2Tipo(aux3);
							t1s = new DatosExpresion(new ExprTipo(tipo3, 1), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
						}
						else
							if (tablas.existeOperacion(Operadores.castingN, tsph.tipo2Token(t1h.getExpr().getTipo()), null) && op4b.equals("castnat")) {
								aux3 = tablas.getTipoResultado(Operadores.castingN, tsph.tipo2Token(t1h.getExpr().getTipo()), null);
								tipo3 = token2Tipo(aux3);
								t1s = new DatosExpresion(new ExprTipo(tipo3, 1), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
							}
							else
								if (tablas.existeOperacion(Operadores.castingE, tsph.tipo2Token(t1h.getExpr().getTipo()), null) && op4b.equals("castent")) {
									aux3 = tablas.getTipoResultado(Operadores.castingE, tsph.tipo2Token(t1h.getExpr().getTipo()), null);
									tipo3 = token2Tipo(aux3);
									t1s = new DatosExpresion(new ExprTipo(tipo3, 1), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
								}
								else {
									erroresC.add(new ErrorContextual("Error de tipado: No se puede realizar el casting sobre " + t1h.getExpr().getTipo().toString() + ". Posicion: "
											+ ta.getPosicion() + "\n"));
									t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "");
								}
				emite(op4b);
				etq = etq + 1;
				for (int i = 0; i < numOp4; i++) {
					emite(op4);
					etq = etq + 1;
				}
			}
			else {
				tokenActual = ta;
				numTkn = listaTokens.indexOf(ta);
				if (hayOp4) {
					t1s = E5(lf, lv, tsph, false, nh);
				}
				else {
					t1s = E5(lf, lv, tsph, parh, nh);
				}
				for (int i = 0; i < numOp4; i++) {
					emite(op4);
					etq = etq + 1;
				}
			}
		}
		else {
			if (hayOp4) {
				t1s = E5(lf, lv, tsph, false, nh);
			}
			else {
				t1s = E5(lf, lv, tsph, parh, nh);
			}
			for (int i = 0; i < numOp4; i++) {
				emite(op4);
				etq = etq + 1;
			}
		}
		if (hayOp4) {
			if (t1s.getExpr().getTipo() == Tipo.err) {
				t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "");
			}
			else
				if (!tablas.existeOperacion(Operadores.not, tsph.tipo2Token(t1s.getExpr().getTipo()), null)
						&& !tablas.existeOperacion(Operadores.unario, tsph.tipo2Token(t1s.getExpr().getTipo()), null)) {
					erroresC.add(new ErrorContextual("Error de tipado: No se puede complementar " + t1s.toString() + ". Posicion: " + getToken().getPosicion() + "\n"));
					t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "");
				}
				else {
					if (tablas.existeOperacion(Operadores.not, tsph.tipo2Token(t1s.getExpr().getTipo()), null)) {
						/*Fact0.cod = parchea(Fact1.cod, Fact1.listaf, Fact1.listav, Fact1.etq, Fact1.etq)*/
						parchea(t1s.getListaf(), t1s.getListav(), etq, etq);
						TipoToken tToken = tablas.getTipoResultado(Operadores.not, tsph.tipo2Token(t1s.getExpr().getTipo()), null);
						Tipo tipo = token2Tipo(tToken);
						t1s = new DatosExpresion(new ExprTipo(tipo, 1), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
					}
					else {
						/*Fact0.cod = parchea(Fact1.cod, Fact1.listaf, Fact1.listav, Fact1.etq, Fact1.etq)*/
						parchea(t1s.getListaf(), t1s.getListav(), etq, etq);
						TipoToken tToken = tablas.getTipoResultado(Operadores.unario, tsph.tipo2Token(t1s.getExpr().getTipo()), null);
						Tipo tipo = token2Tipo(tToken);
						t1s = new DatosExpresion(new ExprTipo(tipo, 1), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
					}
				}
		}
		return t1s;
	}

	/**
	 * Analiza sintacticamente las expresiones de prioridad 5 del programa. Estas expresiones corresponden a los tipos datos primitivos y a los identificadores.
	 * 
	 * @return El tipo (TipoToken) de la expresion
	 * @throws Exception
	 *             Si hay error sintáctico
	 */
	private DatosExpresion E5(ArrayList<Integer> lf, ArrayList<Integer> lv, TablaSimbolos tsph, boolean parh, int nh) throws Exception {
		DatosExpresion t1s = null;
		DatosExpresion t1h;
		if (coincide(TipoToken.parA)) {
			getNextToken();
			t1h = E0(lf, lv, tsph, parh, nh);
			if (!coincide(TipoToken.parC))
				throw new Exception("Se esperaba un ) en la posicion: " + getToken().getPosicion());
			getNextToken();
			t1s = t1h;
		}
		else
			if (coincideDato()) {
				Token aux = tokenActual;
				String op = codigo(getToken().getTipo());
				emite(op + "(" + getToken().getLexema() + ")");
				etq = etq + 1;
				getNextToken();
				t1s = new DatosExpresion(new ExprTipo(token2Tipo(aux.getTipo()), 1), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
			}
			else
				if (coincide(TipoToken.nulo)) {
					String op = codigo(getToken().getTipo());
					emite(op);
					etq = etq + 1;
					getNextToken();
					t1s = new DatosExpresion(new ExprTipo(Tipo.nulo), new ArrayList<Integer>(), new ArrayList<Integer>(), "valor");
				}
				else
					if (coincide(TipoToken.iden)) {
						int nVar = tsph.existeIdAnt(getToken().getLexema(), nh);
						if (nVar == -1) {
							erroresC.add(new ErrorContextual("No existe la variable \"" + getToken().getLexema() + "\"" + getToken().getPosicion() + "\n"));
							t1s = new DatosExpresion(new ExprTipo(Tipo.err), new ArrayList<Integer>(), new ArrayList<Integer>(), "");
							getNextToken();
						}
						else {
							Token aux = tokenActual;
							ExprTipo tInicial = tsph.exprDe(aux.getLexema(), nVar);
							ExprTipo aux2 = D(tInicial, tsph, nVar);
							if (aux2.isError()) {
								t1s = new DatosExpresion(aux2, new ArrayList<Integer>(), new ArrayList<Integer>(), "variable");
							}
							else {
								ArrayList<ParTipos> v = new ArrayList<ParTipos>();
								if ((compatibles(aux2, new ExprTipo(Tipo.nat, 1), tsph, nVar, v) || compatibles(aux2, new ExprTipo(Tipo.booleano, 1), tsph, nVar, v)
										|| compatibles(aux2, new ExprTipo(Tipo.car, 1), tsph, nVar, v) || compatibles(aux2, new ExprTipo(Tipo.real, 1), tsph, nVar, v)
										|| compatibles(aux2, new ExprTipo(Tipo.ent, 1), tsph, nVar, v) || compatibles(aux2, new ExprTipo(Tipo.nulo, 1), tsph, nVar, v))
										&& !parh) {
									emite("apila-ind");
									etq = etq + 1;
								}
							}
							t1s = new DatosExpresion(aux2, new ArrayList<Integer>(), new ArrayList<Integer>(), "variable");
						}
					}
		return t1s;
	}

	// ***********************FUNCIONES AUXILIARES*************************
	/**
	 * Devuelve el codigo máquina a emitir dado un token concreto.
	 * 
	 * @param t
	 *            El token a consultar
	 * @return La cadena (String) a emitir
	 */
	private String codigo(TipoToken t) {
		String op = "";
		switch (t) {
			case not:
				op = "niega";
				break;
			case unario:
				op = "complementario";
				break;
			case castCar:
				op = "castcar";
				break;
			case castEnt:
				op = "castent";
				break;
			case castNat:
				op = "castnat";
				break;
			case castReal:
				op = "castreal";
				break;
			case dder:
				op = "desp-der";
				break;
			case dizq:
				op = "desp-izq";
				break;
			case tNat:
				op = "lee-natural";
				break;
			case tEnt:
				op = "lee-entero";
				break;
			case tReal:
				op = "lee-real";
				break;
			case tChar:
				op = "lee-caracter";
				break;
			case tBool:
				op = "lee-booleano";
				break;
			case menor:
				op = "menor";
				break;
			case mayor:
				op = "mayor";
				break;
			case mayorigual:
				op = "mayoroigual";
				break;
			case menorigual:
				op = "menoroigual";
				break;
			case igual:
				op = "igual";
				break;
			case distinto:
				op = "distinto";
				break;
			case mult:
				op = "multiplica";
				break;
			case div:
				op = "divide";
				break;
			case mod:
				op = "modulo";
				break;
			case and:
				op = "and";
				break;
			case suma:
				op = "suma";
				break;
			case resta:
				op = "resta";
				break;
			case or:
				op = "or";
				break;
			case booleano:
				op = "apila-booleano";
				break;
			case car:
				op = "apila-caracter";
				break;
			case nat:
				op = "apila-natural";
				break;
			case ent:
				op = "apila-entero";
				break;
			case real:
				op = "apila-real";
				break;
			case nulo:
				op = "apila-nulo";
				break;
			default:
				break;
		}
		return op;
	}

	/**
	 * Añade al codigo a generar la cadena introducida
	 * 
	 * @param string
	 *            La cadena a introducir
	 */
	private void emite(String string) {
		this.codString.add(string);
	}

	/**
	 * Devuelve el token actual
	 * @return El token actual
	 */
	private Token getToken() {
		return tokenActual;
	}

	/** Crea una nueva tabla de simbolos
	 * @return Una tabla de simbolos nueva y vacia
	 */
	private TablaSimbolos creaTS() {
		return new TablaSimbolos(new HashMap<ClaveTS, RegistroTS>());
	}

	/** Crea una nueva tabla de simbolos a partir de otra ya existente
	 * @param t La tabla de simbolos que va a ser copiada
	 * @return Una tabla de simbolos nueva con todos los simbolos que contiene la que entra como parametro
	 */
	private TablaSimbolos creaTS(TablaSimbolos t) {
		TablaSimbolos aux = new TablaSimbolos(new HashMap<ClaveTS, RegistroTS>());
		aux.getTs().putAll(t.getTs());
		return aux;
	}

	/** Comprueba si coinciden los tipos de los tokens actual y de entrada
	 * @param t Un token de entrada
	 * @return Verdadero si el token de entrada tiene el mismo tipo que el token actual
	 */
	private boolean coincide(TipoToken t) {
		return (getToken().getTipo() == t);
	}

	/** Comprueba si el token actual es un token identificador de tipo
	 * @return True si es un token de tipo
	 */
	private boolean coincideTipo() {
		return (coincide(TipoToken.tEnt) || coincide(TipoToken.tReal) || coincide(TipoToken.tChar) || coincide(TipoToken.tNat) || coincide(TipoToken.tBool));
	}

	/** Comprueba si el token actual es un token identificador de dato
	 * @return True si es un token de dato
	 */
	private boolean coincideDato() {
		return coincide(TipoToken.booleano) || coincide(TipoToken.nat) || coincide(TipoToken.car) || coincide(TipoToken.real) || coincide(TipoToken.ent);
	}

	/**
	 * Consulta si el token actual es un casting.
	 * 
	 * @return true en caso de serlo, false en otro caso
	 */
	private boolean coincideCasting() {
		return coincide(TipoToken.castCar) || coincide(TipoToken.castEnt) || coincide(TipoToken.castReal) || coincide(TipoToken.castNat);
	}

	/**
	 * Consulta si el token actual es un operador de prioridad 0 (comparativos).
	 * 
	 * @return true en caso de serlo, false en otro caso
	 */
	private boolean coincideOp0() {
		return coincide(TipoToken.menor) || coincide(TipoToken.mayor) || coincide(TipoToken.menorigual) || coincide(TipoToken.mayorigual) || coincide(TipoToken.igual)
				|| coincide(TipoToken.distinto);
	}

	/**
	 * Consulta si el token actual es un operador de prioridad 1 (aditivos).
	 * 
	 * @return true en caso de serlo, false en otro caso
	 */
	private boolean coincideOp1() {
		return coincide(TipoToken.or) || coincide(TipoToken.suma) || coincide(TipoToken.resta);
	}

	/**
	 * Consulta si el token actual es un operador de prioridad 2 (multiplicativos).
	 * 
	 * @return true en caso de serlo, false en otro caso
	 */
	private boolean coincideOp2() {
		return coincide(TipoToken.and) || coincide(TipoToken.mult) || coincide(TipoToken.div) || coincide(TipoToken.mod);
	}

	/**
	 * Consulta si el token actual es un operador de prioridad 3 (desplazamientos).
	 * 
	 * @return true en caso de serlo, false en otro caso
	 */
	private boolean coincideOp3() {
		return coincide(TipoToken.dder) || coincide(TipoToken.dizq);
	}

	/**
	 * Consulta si el token actual es un operador de prioridad 4 (operador unario).
	 * 
	 * @return true en caso de serlo, false en otro caso
	 */
	private boolean coincideOp4() {
		return coincide(TipoToken.resta) || coincide(TipoToken.not);
	}

	/** Comprueba si el token actual es un token identificador de separador
	 * @return True si es un token separador
	 */
	private boolean coincideSeparador() {
		return coincide(TipoToken.asig) || coincideOp0() || coincideOp1() || coincideOp2() || coincideOp3() || coincideOp4() || coincide(TipoToken.EOF) || coincide(TipoToken.puntoycoma)
				|| coincide(TipoToken.parC) || coincide(TipoToken.llaveC) || coincide(TipoToken.coma) || coincide(TipoToken.corC);
	}

	/**
	 * Consulta si el token actual es un operador de prioridad 4 (casting).
	 * 
	 * @return true en caso de serlo, false en otro caso
	 */
	private boolean coincideOp4b() {
		return coincide(TipoToken.castCar) || coincide(TipoToken.castEnt) || coincide(TipoToken.tReal) || coincide(TipoToken.castNat);
	}

	public ArrayList<ErrorContextual> getErroresC() {
		return erroresC;
	}

	public void setErroresC(ArrayList<ErrorContextual> erroresC) {
		this.erroresC = erroresC;
	}

	/** Sustituye un salto que se habia dejado sin rellenar por su destino real
	 * @param instSalto La instruccion de salto a parchear
	 * @param destSalto El destino del salto
	 */
	private void parchea(int instSalto, int destSalto) {
		String aux = this.codString.get(instSalto).replace("?", Integer.toString(destSalto));
		this.codString.remove(instSalto);
		this.codString.add(instSalto, aux);
	}

	/** Parchea dos listas de saltos a dos destinos diferentes
	 * @param listaf La primera lista de instrucciones de salto a parchear
	 * @param listav La segunda lista de instrucciones de salto a parchear
	 * @param ef El destino de las instrucciones de la primera lista
	 * @param ev El destino de las instrucciones de la segunda lista
	 */
	private void parchea(ArrayList<Integer> listaf, ArrayList<Integer> listav, int ef, int ev) {
		if (!(listav == null))
			for (int i = 0; i < listav.size(); i++)
				parchea(listav.get(i), ev);
		if (!(listaf == null))
			for (int i = 0; i < listaf.size(); i++)
				parchea(listaf.get(i), ef);
	}

	private boolean referenciaErronea(ExprTipo e, TablaSimbolos tsph, int nh) {
		return e.getTipo() == ExprTipo.Tipo.ref && !tsph.existeId(e.getId(), nh);
	}

	private Tipo token2Tipo(TipoToken t) {
		Tipo resp;
		if (t == null)
			return null;
		switch (t) {
			case tBool:
				resp = Tipo.booleano;
				break;
			case tChar:
				resp = Tipo.car;
				break;
			case tNat:
				resp = Tipo.nat;
				break;
			case tReal:
				resp = Tipo.real;
				break;
			case tEnt:
				resp = Tipo.ent;
				break;
			case nat:
				resp = Tipo.nat;
				break;
			case real:
				resp = Tipo.real;
				break;
			case ent:
				resp = Tipo.ent;
				break;
			case car:
				resp = Tipo.car;
				break;
			case booleano:
				resp = Tipo.booleano;
				break;
			case vector:
				resp = Tipo.array;
				break;
			case registro:
				resp = Tipo.rec;
				break;
			case puntero:
				resp = Tipo.punt;
				break;
			case procedimiento:
				resp = Tipo.proc;
				break;
			case ERROR:
				resp = Tipo.err;
				break;
			case nulo:
				resp = Tipo.punt;
				break;
			default:
				resp = Tipo.ref;
				break;
		}
		return resp;
	}

	/**
	 * Comprueba si dos objetos de tipo ExprTipo son compatibles (lados izquierdo y derecho de una asignacion)
	 * @param e1 El primer objeto a comprobar
	 * @param e2 El segundo objeto a comprobar
	 * @param tsph La tabla de simbolos
	 * @param nh El nivel
	 * @param v Un array de pares de tipos
	 * @return True si son compatibles, false en otro caso
	 */
	private boolean compatibles(ExprTipo e1, ExprTipo e2, TablaSimbolos tsph, int nh, ArrayList<ParTipos> v) {
		ParTipos aux = new ParTipos(e1, e2);
		if (v.contains(aux))
			return true;
		else
			v.add(aux);
		if ((e1.getTipo() == Tipo.booleano && e2.getTipo() == Tipo.booleano) || (e1.getTipo() == Tipo.car && e2.getTipo() == Tipo.car)
				|| (e1.getTipo() == Tipo.nat && e2.getTipo() == Tipo.nat) || (e1.getTipo() == Tipo.ent && e2.getTipo() == Tipo.ent)
				|| (e1.getTipo() == Tipo.real && e2.getTipo() == Tipo.real))
			return true;
		else
			if (e1.getTipo() == Tipo.ref) {
				int nTipo = tsph.existeIdAnt(e1.getId(), nh);
				return compatibles(tsph.exprDe(e1.getId(), nTipo), e2, tsph, nh, v);
			}
			else
				if (e2.getTipo() == Tipo.ref) {
					int nTipo = tsph.existeIdAnt(e2.getId(), nh);
					return compatibles(e1, tsph.exprDe(e2.getId(), nTipo), tsph, nh, v);
				}
				else
					if (e1.getTipo() == Tipo.array && e2.getTipo() == Tipo.array) {
						if (e1.getNumElems() == e2.getNumElems()) {
							return compatibles(e1.getTipoBase(), e2.getTipoBase(), tsph, nh, v);
						}
						else {
							erroresC.add(new ErrorContextual("El tamaño de los arrays es distinto.\n"));
						}
					}
					else
						if ((e1.getTipo() == Tipo.rec && e2.getTipo() == Tipo.rec)) {
							ExprTipo eR1 = e1;
							ExprTipo eR2 = e2;
							if (eR1.getCampos().getLista().size() == eR2.getCampos().getLista().size()) {
								for (int i = 0; i < e1.getCampos().getLista().size(); i++)
									if (!compatibles(e1.getCampos().getLista().get(i).getTipo(), e2.getCampos().getLista().get(i).getTipo(), tsph, nh, v))
										return false;
								return true;
							}
							else
								return false;
						}
						else
							if ((e1.getTipo() == Tipo.punt && e2.getTipo() == Tipo.nulo) || (e2.getTipo() == Tipo.punt && e1.getTipo() == Tipo.nulo)) {
								return true;
							}
							else
								if ((e1.getTipo() == Tipo.punt && e2.getTipo() == Tipo.punt)) {
									return compatibles(e1.getTipoBase(), e2.getTipoBase(), tsph, nh, v);
								}
		return false;
	}

	/** Devuelve el tipo referenciado
	 * @param tsph La tabla de simbolos
	 * @param e El objeto ExprTipo que contiene la informacion de la expresion
	 * @param nh El nivel
	 * @return Un objeto de tipo ExprTipo con la informacion del tipo de la expresion final
	 */
	private ExprTipo ref(TablaSimbolos tsph, ExprTipo e, int nh) {
		if (e.getTipo() == ExprTipo.Tipo.ref) {
			String aux = e.getId();
			int nf = nh;
			for (int i = nh; i >= 0; i--) {
				if (tsph.existeId(aux, i)) {
					nf = i;
				}
			}
			if (tsph.existeId(aux, nf)) {
				ExprTipo aux2 = ref(tsph, tsph.exprDe(aux, nf), nf);
				return aux2;
			}
			else
				return new ExprTipo(ExprTipo.Tipo.err, 0);
		}
		else
			return e;
	}
	
	//TODO De aqui en adelante sin Javadocs escritos
	private void accesoVar(RegistroTS reg) {
		int a = 1 + reg.getNivel();
		emite("apila-dir(" + a + ")");
		emite("apila-natural(" + reg.dameDir() + ")");
		emite("suma");
		if (reg.dameClase() == TipoToken.paramvar)
			emite("apila-ind");
	}

	private int longAccesoVar(RegistroTS reg) {
		if (reg.dameClase() == TipoToken.paramvar)
			return 4;
		else
			return 3;
	}

	private void inicio(int numNiveles, int tamDatos) {
		emite("apila-natural(?)");// numNiveles +2
		emite("desapila-dir(1)");
		emite("apila-natural(?)");// 1+numNiveles+Datos
		emite("desapila-dir(0)");
	}

	private void avanza(TipoToken t) {
		while (tokenActual.getTipo() != t && tokenActual.getTipo() != TipoToken.EOF) {
			getNextToken();
		}
	}

	private void avanzaCierreInstruccion() {
		while (tokenActual.getTipo() != TipoToken.EOF && tokenActual.getTipo() != TipoToken.puntoycoma && tokenActual.getTipo() != TipoToken.llaveC) {
			getNextToken();
		}
	}

	private void avanzaCierre() {
		while (!coincideSeparador()) {
			getNextToken();
		}
	}

	private void prologo(int nivel, int tamlocales) {
		emite("apila-dir(0)");
		emite("apila-natural(2)");
		emite("suma");
		emite("apila-dir(" + (1 + nivel) + ")");
		emite("desapila-ind-natural");
		emite("apila-dir(0)");
		emite("apila-natural(3)");
		emite("suma");
		emite("desapila-dir(" + (1 + nivel) + ")");
		emite("apila-dir(0)");
		emite("apila-natural(" + (tamlocales + 2) + ")");
		emite("suma");
		emite("desapila-dir(0)");
	}

	private void epilogo(int nivel) {
		emite("apila-dir(" + (1 + nivel) + ")");
		emite("apila-natural(2)");
		emite("resta");
		emite("apila-ind");
		emite("apila-dir(" + (nivel + 1) + ")");
		emite("apila-natural(3)");
		emite("resta");
		emite("copia");
		emite("desapila-dir(0)");
		emite("apila-natural(2)");
		emite("suma");
		emite("apila-ind");
		emite("desapila-dir(" + (nivel + 1) + ")");
	}

	private void apilaRet(int ret) {
		emite("apila-dir(0)");
		emite("apila-natural(1)");
		emite("suma");
		emite("apila-natural(?)");/*? seria ret pero hay que parchearlo*/
		emite("desapila-ind-natural");
	}

	private void inicioPaso() {
		emite("apila-dir(0)");
		emite("apila-natural(3)");
		emite("suma");
	}

	private void pasoParametro(String modoReal, ExprParam p) {
		if (p.getModo() == "valor" && modoReal == "variable") {
			emite("mueve(" + p.getTipo().getTamaño() + ")");
		}
		else {
			emite("desapila-ind-natural");
		}
	}

	private int longPasoParametro(String modoReal, ExprParam p) {
		return 1;
	}

	private void direccionParFormal(ExprParam p) {
		emite("apila-natural(" + p.getDir() + ")");
		emite("suma");
	}

	private int longDireccionParFormal(ExprParam p) {
		return 2;
	}

	private boolean hayOp0() {
		int t = numTkn;
		boolean esSeparador = false;
		TipoToken aux;
		while (!esSeparador && t < this.listaTokens.size()) {
			aux = this.listaTokens.get(t).getTipo();
			if ((aux == TipoToken.mayor) || (aux == TipoToken.mayorigual) || (aux == TipoToken.menor) || (aux == TipoToken.menorigual) || (aux == TipoToken.igual)
					|| (aux == TipoToken.distinto))
				return true;
			else
				if ((aux == TipoToken.puntoycoma)  || (aux == TipoToken.parC) || (aux == TipoToken.llaveC) || (aux == TipoToken.coma)
						|| (aux == TipoToken.EOF) || (aux == TipoToken.asig) || (aux == TipoToken.corC))
					esSeparador = true;
			t++;
		}
		return false;
	}

	private boolean hayOp1() {
		int t = numTkn;
		boolean esSeparador = false;
		TipoToken aux;
		while (!esSeparador && t < this.listaTokens.size()) {
			aux = this.listaTokens.get(t).getTipo();
			if ((aux == TipoToken.suma) || (aux == TipoToken.resta) || (aux == TipoToken.or))
				return true;
			else
				if ((aux == TipoToken.puntoycoma)  || (aux == TipoToken.parC) || (aux == TipoToken.llaveC) || (aux == TipoToken.coma)
						|| (aux == TipoToken.EOF) || (aux == TipoToken.asig) || (aux == TipoToken.corC))
					esSeparador = true;
			t++;
		}
		return false;
	}

	private boolean hayOp2() {
		int t = numTkn;
		boolean esSeparador = false;
		TipoToken aux;
		while (!esSeparador && t < this.listaTokens.size()) {
			aux = this.listaTokens.get(t).getTipo();
			if ((aux == TipoToken.and) || (aux == TipoToken.mult) || (aux == TipoToken.div) || (aux == TipoToken.mod))
				return true;
			else
				if ((aux == TipoToken.puntoycoma)  || (aux == TipoToken.parC) || (aux == TipoToken.llaveC) || (aux == TipoToken.coma)
						|| (aux == TipoToken.EOF) || (aux == TipoToken.asig) || (aux == TipoToken.corC))
					esSeparador = true;
			t++;
		}
		return false;
	}

	private boolean hayOp3() {
		int t = numTkn;
		boolean esSeparador = false;
		TipoToken aux;
		while (!esSeparador && t < this.listaTokens.size()) {
			aux = this.listaTokens.get(t).getTipo();
			if ((aux == TipoToken.dder) || (aux == TipoToken.dizq))
				return true;
			else
				if ((aux == TipoToken.puntoycoma)  || (aux == TipoToken.parC) || (aux == TipoToken.llaveC) || (aux == TipoToken.coma)
						|| (aux == TipoToken.EOF) || (aux == TipoToken.asig) || (aux == TipoToken.corC))
					esSeparador = true;
			t++;
		}
		return false;
	}
}
